💡 博主前言 :本文整理自个人的 408 计算机考研复习笔记。本书写特点是拒绝死记硬背,用最直观的大白话提炼算法的核心直觉。笔记中针对高频考点进行了梳理,并对邻接矩阵幂次原理、Prim 与 Dijkstra 算法的"集合思想"进行了深度扩展与全流程图解。希望能帮到一起备考的伙伴们!
文章目录
-
- [📌 一、 图的基本概念与核心性质](#📌 一、 图的基本概念与核心性质)
-
- [1. 基础定义与边数关系](#1. 基础定义与边数关系)
- [2. 路径与环](#2. 路径与环)
- [3. 子图与连通性](#3. 子图与连通性)
- [💾 二、 图的存储结构与底层原理](#💾 二、 图的存储结构与底层原理)
-
- [1. 邻接矩阵(Adjacency Matrix)](#1. 邻接矩阵(Adjacency Matrix))
-
- [🔍 【高频探究】邻接矩阵幂次 A k i j A^kij Akij 的物理意义与原理](#🔍 【高频探究】邻接矩阵幂次 A k [ i ] [ j ] A^k[i][j] Ak[i][j] 的物理意义与原理)
- [2. 邻接表(Adjacency List)](#2. 邻接表(Adjacency List))
- [3. 其他高效存储结构(408常考选填题)](#3. 其他高效存储结构(408常考选填题))
- [🎯 三、 图的遍历算法直觉(大白话总结)](#🎯 三、 图的遍历算法直觉(大白话总结))
-
- [1. DFS(深度优先搜索)](#1. DFS(深度优先搜索))
- [2. BFS(广度优先搜索)](#2. BFS(广度优先搜索))
- [🛠️ 四、 图的核心算法:基于"集合思想"的深度对决](#🛠️ 四、 图的核心算法:基于“集合思想”的深度对决)
-
- [📝 【红字突破】Dijkstra 与 Prim "集合思想"的同台对比例题](#📝 【红字突破】Dijkstra 与 Prim “集合思想”的同台对比例题)
-
- [1. 实例场景设定](#1. 实例场景设定)
- [2. 第一步(Prim 与 Dijkstra 决策相同)](#2. 第一步(Prim 与 Dijkstra 决策相同))
- [3. 第二步(分歧产生的命运转折点!)](#3. 第二步(分歧产生的命运转折点!))
-
- [🌳 方案 A:Prim 算法(目标:连通全图,怎么省钱怎么来)](#🌳 方案 A:Prim 算法(目标:连通全图,怎么省钱怎么来))
- [🛣️ 方案 B:Dijkstra 算法(目标:每个人回老家 V 0 V_0 V0 的车费最便宜)](#🛣️ 方案 B:Dijkstra 算法(目标:每个人回老家 V 0 V_0 V0 的车费最便宜))
- [💡 核心总结](#💡 核心总结)
📌 一、 图的基本概念与核心性质
1. 基础定义与边数关系
-
弧头与弧尾(有向边) :有向边表示为 ⟨ v 1 , v 2 ⟩ \langle v_1, v_2 \rangle ⟨v1,v2⟩,有向边的终点叫弧头 (Arrowhead),起点叫弧尾(Arrowtail)。
-
示例 :对于有向边 v 1 → v 2 v_1 \to v_2 v1→v2, v 2 v_2 v2 是弧头, v 1 v_1 v1 是弧尾。
-
顶点的度、入度与出度:
-
无向图 :顶点的度数之和等于边数的 2 倍,即 ∑ D ( v ) = 2 e \sum D(v) = 2e ∑D(v)=2e。
-
有向图:
-
总入度 = 总出度 = 边数 e e e,即 ∑ I D ( v ) = ∑ O D ( v ) = e \sum ID(v) = \sum OD(v) = e ∑ID(v)=∑OD(v)=e。
-
有向图中顶点的度数之和为入度与出度之和,即 ∑ D ( v ) = 2 × 入度 = 2 × 出度 = 2 e \sum D(v) = 2 \times \text{入度} = 2 \times \text{出度} = 2e ∑D(v)=2×入度=2×出度=2e。
-
完全图的边数上限公式:
-
无向完全图 : n n n 个顶点,任意两点间都有一条边 ⇒ \Rightarrow ⇒ 边数 e = n ( n − 1 ) 2 e = \frac{n(n-1)}{2} e=2n(n−1)。
-
有向完全图 : n n n 个顶点,任意两点间都有方向相反的两条有向边 ⇒ \Rightarrow ⇒ 边数 e = n ( n − 1 ) e = n(n-1) e=n(n−1)。
2. 路径与环
- 路径 :顶点序列 v 1 , v 2 , v 3 ... v_1, v_2, v_3 \dots v1,v2,v3... 构成的序列。若序列长度为 2,则包含 2 条边。
- 回路(环):起点和终点是同一个顶点的路径。
3. 子图与连通性
-
子图 :顶点集和边集分别是父图的顶点集和边集的子集的图。(形象记忆:树苗是整棵大树的子图)。
-
连通图(无向图):图中任意两个顶点之间都是连通的(即存在路径)。
-
极大连通子图(连通分量):无向图中的非连通图,其极大的连通子部分。若任意添加一个顶点都会导致图变成非连通,则该子图为极大连通子图。
-
极小连通子图(生成树) :包含图中全部 n n n 个顶点和 n − 1 n-1 n−1 条边的极小连通子图。
-
🔥 核心直觉 :若少一条边,则图不再连通;若多一条边,则图中必产生环。
-
生成森林:由多个连通分量的生成树组成的非连通图。
-
强连通图(有向图):有向图中任意两个顶点都存在双向路径(路径中可以包含中转顶点)。
-
强连通分量 :有向图中的极大强连通子图。
💾 二、 图的存储结构与底层原理
1. 邻接矩阵(Adjacency Matrix)
-
存储方式:
-
顶点数组:
v[n](Vertex 顶点集) -
边数组:
e[n][n](Edges 边集,二维数组) -
特点 :适用于稠密图,空间利用率高。
-
对称性 :无向图 的邻接矩阵都是对称矩阵,满足 A = A T A = A^T A=AT,在实际工程中可以采用压缩存储(只存上三角或下三角)来节省空间。
🔍 【高频探究】邻接矩阵幂次 A k i j A^kij Akij 的物理意义与原理
❓ 思考红字 :若邻接矩阵为 A A A,那么 e d g e s k i j edges^kij edgeskij 表示从顶点 i i i 到顶点 j j j 之间长度为 k k k 的路径条数。它的底层数学原理是什么?
原理剖析 :我们以 k = 2 k=2 k=2 时的矩阵乘法公式展开:
A 2 i j = ∑ m = 1 n A i m × A m j A^2ij = \sum_{m=1}^{n} Aim \times Amj A2ij=m=1∑nAim×Amj
- A i m = 1 Aim = 1 Aim=1 表示存在边 i → m i \to m i→m;
- A m j = 1 Amj = 1 Amj=1 表示存在边 m → j m \to j m→j;
- 只有当 A i m × A m j = 1 Aim \times Amj = 1 Aim×Amj=1 时,才说明存在一条通过中转点 m m m 的路径 i → m → j i \to m \to j i→m→j,其长度恰好为 2。
- 通过 ∑ \sum ∑ 对所有可能的中转点 m m m 进行求和,得到的便是从 i i i 到 j j j 长度为 2 的路径总条数。以此类推,通过数学归纳法可证明 A k i j A^kij Akij 即为长度为 k k k 的路径条数。
2. 邻接表(Adjacency List)
-
空间复杂度:
-
有向图: O ( n + e ) O(n+e) O(n+e)
-
无向图: O ( n + 2 e ) O(n+2e) O(n+2e) (因为每条无向边在表中被记录了两次)
-
🔥 出度/入度查询方法总结:
-
顶点的出度 极其容易获取,就是该顶点挂载的单链表中边结点的个数。
-
顶点的入度 则较为麻烦,需要遍历整个邻接表的所有链表才能统计出来。
3. 其他高效存储结构(408常考选填题)
- 十字链表:用于优化有向图,能同时在常数或极短时间内获取出度和入度。
- 邻接多重表:用于优化无向图,方便对边进行标记、删除等操作。
🎯 三、 图的遍历算法直觉(大白话总结)
对于图的深度优先(DFS)与广度优先(BFS)遍历,可以用两句极简的"算法直觉"来概括本质:
1. DFS(深度优先搜索)
- 🔥 方法总结 :"只要不成环,一直找邻居 ⟶ \longrightarrow ⟶"
- 算法直觉:这是一种"一条路走到黑"的贪心回溯思想。利用了栈(Stack)或递归的特性,只要当前顶点的邻居节点没有被访问过(不成环),就立刻深入访问邻居的邻居,直到撞到南墙再回溯。
2. BFS(广度优先搜索)
- 🔥 方法总结 :"逐层遍历 🎯"
- 算法直觉:以初始起点为核心,像水波纹一样一层一层向外扩散。利用了队列(Queue)的特性,先访问当前节点的所有直接邻居(第一层),再依次访问邻居的邻居(第二层)。
🛠️ 四、 图的核心算法:基于"集合思想"的深度对决
在考研中,Prim算法 、Kruskal算法 与Dijkstra算法是手算模拟和算法理解的重中之重。下面先给出精炼总结,随后展开详细的例题对比。
-
🔥 Prim(普里姆)算法:从初始顶点出发,逐个寻找离"这一集合"(集合初始只有起点)最近的点,且把这一点合并进集合,直到所有点都归并到集合。
-
特质 :加点法,时间复杂度与边数无关,适合稠密图。
-
🔥 Kruskal(克鲁斯卡尔)算法 :从最短边搭起,每一步连接当下最短的边(不允许成环),直到所有点都被连通。
-
特质 :加边法,通常采用并查集判环,适合稀疏图。
-
🔥 Dijkstra(迪杰斯特拉)算法:与 Prim 一样,基于"集合"的思想。
-
特质:单源最短路径算法。
📝 【红字突破】Dijkstra 与 Prim "集合思想"的同台对比例题
虽然 Prim 和 Dijkstra 都在执行"将红点(未加入集合)变为蓝点(已加入集合)"的操作,且都用到了贪心策略,但它们的更新代价(Distance)的逻辑有着本质的区别。
1. 实例场景设定
假设图中有三个顶点: V 0 V_0 V0(源点/起点)、 V 1 V_1 V1、 V 2 V_2 V2。
各边权重如下:
- V 0 → V 1 V_0 \to V_1 V0→V1 = 2
- V 0 → V 2 V_0 \to V_2 V0→V2 = 10
- V 1 → V 2 V_1 \to V_2 V1→V2 = 3
我们将已选入的顶点划入集合 S S S,未选入的划入集合 V − S V-S V−S。初始状态下, S = { V 0 } S = \{V_0\} S={V0}。
2. 第一步(Prim 与 Dijkstra 决策相同)
- 此时未加入集合的点中,离 V 0 V_0 V0 最近的是 V 1 V_1 V1(距离为 2)。
- 将 V 1 V_1 V1 收入集合,此时已选集合 S = { V 0 , V 1 } S = \{V_0, V_1\} S={V0,V1}。
3. 第二步(分歧产生的命运转折点!)
现在我们需要决定如何将最后一个点 V 2 V_2 V2 纳入麾下。这时候两个算法在"计算距离"时展现出了完全不同的脑回路:
🌳 方案 A:Prim 算法(目标:连通全图,怎么省钱怎么来)
-
计算逻辑 : V 2 V_2 V2 距离整个已选集合 S { V 0 , V 1 } S \{V_0, V_1\} S{V0,V1} 这个整体的最短单条边是谁?
-
看 V 0 → V 2 V_0 \to V_2 V0→V2 边权是 10;
-
看 V 1 → V 2 V_1 \to V_2 V1→V2 边权是 3。
-
做出决策 :通过 V 1 V_1 V1 连过去明显更省边权!所以 Prim 认为 V 2 V_2 V2 当前的候选距离是 3。
-
最终结果 :选择边 ( V 1 , V 2 ) (V_1, V_2) (V1,V2),最小生成树(MST)的总权重 = 2 + 3 = 5 2 + 3 = 5 2+3=5。
🛣️ 方案 B:Dijkstra 算法(目标:每个人回老家 V 0 V_0 V0 的车费最便宜)
-
计算逻辑 : V 2 V_2 V2 跨过集合后,回到"源点 V 0 V_0 V0"的总累加路径长度是多少?
-
直达路线 V 0 → V 2 V_0 \to V_2 V0→V2:总长 10;
-
中转路线 V 0 → V 1 → V 2 V_0 \to V_1 \to V_2 V0→V1→V2:总长 = ( V 0 → V 1 ) + ( V 1 → V 2 ) = 2 + 3 = 5 (V_0 \to V_1) + (V_1 \to V_2) = 2 + 3 = 5 (V0→V1)+(V1→V2)=2+3=5。
-
做出决策 :走中转路线总里程更短!Dijkstra 执行松弛操作(Relaxation) : D V 2 = min ( 10 , 2 + 3 ) = 5 DV_2 = \min(10, 2+3) = 5 DV2=min(10,2+3)=5。Dijkstra 认为 V 2 V_2 V2 的最短路径值是 5。
-
最终结果 :求得 V 0 V_0 V0 到 V 2 V_2 V2 的最短路径长度为 5。
💡 核心总结
- Prim 算法 :每次向外探摸时,只关心红点到已选集合边界的单条最省边长(局部最优连通)。
- Dijkstra 算法 :每次向外探摸时,关心的是红点越过边界后,一路走回源点的总累加里程(全局最优路径)。
通过这种大白话的模型构建,无论是面对 408 考研中的选择题模拟,还是大题的手算,都能做到数感清晰,准确率百分之百!