板子:判断路径、判断有环、判断树
三个题都是 DFS 的变形:判路径就是看从
出发能不能走到 ;有向图判环要区分「正在访问」和「已经访问完」;判树就是连通而且边数等于 。
代码
bool existPath(MGraph &G, int i, int j) { // i 到 j 是否有路径;调用前把 visited 全部清零
if (i == j) return true; // 走到了
visited[i] = true;
for (int w = 0; w < G.numVertices; w++)
if (G.Edge[i][w] != 0 && !visited[w] && existPath(G, w, j))
return true; // 从某个邻接点能走到 j,就一路返回 true
return false;
}
int color[MAXV]; // 0:还没访问;1:正在访问(还在递归栈里);2:已经访问完
bool dfsCycle(MGraph &G, int v) { // 从 v 出发,看能不能找到环
color[v] = 1;
for (int w = 0; w < G.numVertices; w++)
if (G.Edge[v][w] != 0) {
if (color[w] == 1) return true; // 走回了递归栈里的顶点:出现回边,有环
if (color[w] == 0 && dfsCycle(G, w)) return true;
}
color[v] = 2; // v 能到的顶点都走完了,没发现环
return false;
}
bool hasCycleDG(MGraph &G) { // 有向图是否有环
for (int i = 0; i < G.numVertices; i++) color[i] = 0;
for (int i = 0; i < G.numVertices; i++) // 非连通图:每个没访问过的顶点都要出发一次
if (color[i] == 0 && dfsCycle(G, i)) return true;
return false;
}
int dfsCount(MGraph &G, int v) { // 从 v 出发能访问到的顶点个数
visited[v] = true;
int cnt = 1; // v 自己
for (int w = 0; w < G.numVertices; w++)
if (G.Edge[v][w] != 0 && !visited[w]) cnt += dfsCount(G, w);
return cnt;
}
bool isTree(MGraph &G) { // 无向图是一棵树 ⇔ 连通,而且边数 = n - 1
int n = G.numVertices, e = 0;
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++) // 无向图的每条边在矩阵里出现两次,只数上三角
if (G.Edge[i][j] != 0) e++;
if (e != n - 1) return false;
for (int i = 0; i < n; i++) visited[i] = false;
return dfsCount(G, 0) == n; // 从 0 出发能走遍所有顶点,就是连通的
}visited[] 用的是 DFS 与 BFS 里定义的全局数组。
复杂度:邻接矩阵上都是时间
关键边界:有向图判环为什么要三种颜色
| 碰到的邻接点 | 说明 | 处理 |
|---|---|---|
color[w] == 0 | 还没访问过 | 递归下去 |
color[w] == 1 | 有环 | |
color[w] == 2 | 不是环,跳过 |
只用 visited 两种状态会误判。例如
其他判环方法
- 有向图:用 拓扑排序,输出的顶点不足
个就有环。 - 无向图:DFS 时碰到一个已访问、而且不是自己双亲的顶点,就有环。也可以直接数边:无向图无环当且仅当 边数
连通分量数。
易错点
existPath调用前要把visited清零,否则上一次遍历的标记会让它提前返回false。isTree只数上三角。数整个矩阵的话,边数会多一倍。
链接
- 📕 返回:数据结构代码板子
- ⬅️ 上一篇:BFS 求无权图单源最短路
- ➡️ 下一篇:Floyd
- 🔗 6.3 图的遍历