17. 图环
图分为有向图(Directed Graph,Digraph)和无向图(Undirected Graph),区别在于节点之间的边是否有方向。
问题 :检测连通图中是否有环。
17.1. 无向图
深度优先遍历 (Depth-First Search Traversal)
标记法,使用 visited 数组对图中的所有顶点定义三种状态:
顶点未被访问过(0)
顶点刚开始被访问(1)
顶点被访问过,并且其所有邻接点也被访问过(2)
另外,使用 father 数组记录 DFS 过程中所有顶点的父节点。
判断:若在 DFS 过程中遇到“回边”(即指向已经访问过的顶点的边,父节点除外),则存在环。
1void DFS(int v, Graph G) 2{ 3 visited[v] = 1; // 开始访问节点 v 4 for(int i = 0 ; i < G.vertexNum; i++) 5 { 6 if(i != v && G.arc[v][i] != INF) // 邻接矩阵中节点 v 的邻接点(邻接矩阵表示法) 7 { 8 if(visited[i] == 1 && father[v] != i) 9 // i 不是父节点,而且还被访问过(状态 1,说明不是回溯过来的顶点),说明存在环 10 // visited[i] == 2 不行: 11 // 对无向图而言,v 也是 i 的邻接点,意味着 i -> v 早就访问过,不会再重复访问 v -> i,因此不会出现 visited[i] == 2 12 // 对有向图而言,说明 i 是 v 和其他节点的公共子节点,不构成环 13 { 14 cout<<"图存在环"; 15 int temp = v; 16 while(temp != i) 17 { 18 cout<< temp << " <- "; // 输出环 19 temp = father[temp]; 20 } 21 cout<< i <<endl; 22 } 23 else 24 if(visited[i] == 0) 25 { 26 father[i] = v; // 更新 father 数组 27 DFS(i, G); 28 } 29 30 } 31 } 32 visited[v] = 2; // 遍历完 v 所有的邻接点,变为状态 2 33}
广度优先遍历 (Breadth-First Search Traversal)
与 DFS 类似,需要使用 father 数组记录 BFS 过程中所有顶点的父节点。
visited 数组只需要记录两种状态:未访问和已访问。
17.2. 有向图
拓扑排序 (Topological Sorting)
见本章第 10 节。
深度优先遍历 (Depth-First Search Traversal)
与无向图的 DFS 相同。
17.3. 参考资料
判断无向图/有向图中是否存在环