图 = 顶点集 + 边集,边可带方向与权值;「多对多」关系。
| 表示 | 结构 | 空间 | 判断边 | 遍历邻居 |
|---|---|---|---|---|
| 邻接矩阵 | V×V 二维数组 | O(V²) | O(1) | O(V) |
| 邻接表 | 每个顶点一个链表 | O(V+E) | O(deg) | O(deg) |
#define MAXV 1000
// 邻接表:V 个顶点的链表头数组,adj[u] 是 u 的出边链表头
typedef struct Edge { int to, w; struct Edge *next; } Edge;
Edge *adj[MAXV];
DFS 沿一条路走到底再回溯(递归或显式栈),BFS 逐层扩散(队列)。
int visited[MAXV];
void dfs(int u) {
visited[u] = 1;
for (Edge *e = adj[u]; e; e = e->next)
if (!visited[e->to]) dfs(e->to);
}
BFS 用队列(第 3 章),首次到达某顶点时的层数就是无权最短路。DFS 产生深度优先森林,用于拓扑排序、强连通分量;BFS 用于最短路径、二分图判定。
连通带权无向图的生成树中,边权和最小的那棵。
// Prim:从点出发,每次把「离当前树最近」的点并入(贪心 + 优先队列,见第 5 章堆)
// Kruskal:把所有边按权排序,用并查集(第 11 章)判断加入是否成环
| Prim | Kruskal | |
|---|---|---|
| 起点 | 顶点 | 边 |
| 依赖 | 优先队列 | 排序 + 并查集 |
| 复杂度 | O(E log V) | O(E log E) |
| 适合 | 稠密图 | 稀疏图 |
Dijkstra:单源最短路径,要求边权非负。每次选「当前距离最小」的未确定点,松弛它的邻居,O(E log V)。
// dist[v] 记录源到 v 的最短距离;每次取未确定中最小者,用优先队列优化
Floyd:多源最短路径,三重循环动态规划(第 10 章),允许负权边(无负环),O(V³)。
// dp[k][i][j]:只经过前 k 个点的 i→j 最短路,压掉 k 维
for (int k = 0; k < V; k++)
for (int i = 0; i < V; i++)
for (int j = 0; j < V; j++)
if (d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j];
对比:矩阵 O(1) 判边但 O(V²) 空间;邻接表 O(V+E) 省空间但判边扫链表。稠密选矩阵,稀疏选表。
对比:两者皆贪心(第 10 章);Prim 宜稠密图,Kruskal 宜稀疏图,多依赖并查集判环。
对比:Dijkstra 单源、需非负权、O(E log V);Floyd 求所有点对、支持负权(无负环)、O(V³)。