▶ 书中第四章部分程序,加上自己补充的代码,图的环相关
● 无向图中寻找环
1 package package01; 2 3 import edu.princeton.cs.algs4.In; 4 import edu.princeton.cs.algs4.StdOut; 5 import edu.princeton.cs.algs4.Graph; 6 import edu.princeton.cs.algs4.Stack; 7 8 public class class01 9 { 10 private boolean[] marked; 11 private int[] edgeTo; 12 private Stack<Integer> cycle; // 用来存储环的顶点 13 14 public class01(Graph G) 15 { 16 if (hasSelfLoop(G)) 17 return; 18 if (hasParallelEdges(G)) 19 return; 20 marked = new boolean[G.V()]; 21 edgeTo = new int[G.V()]; 22 for (int v = 0; v < G.V(); v++) 23 { 24 if (!marked[v]) 25 dfs(G, -1, v); // 首次调用时参数 u 要赋成非定点编号的值 26 } 27 } 28 29 private void dfs(Graph G, int u, int v) // 深度优先探索,传入起点 v 及其父顶点 u 30 { 31 marked[v] = true; 32 for (int w : G.adj(v)) 33 { 34 if (cycle != null) // 已经找到环,停止搜索(只搜一个就停) 35 return; 36 if (!marked[w]) 37 { 38 edgeTo[w] = v; 39 dfs(G, v, w); 40 } 41 else if (w != u) // 顶点 w 已经遍历,且边 w-v 不是来边 u-v,说明成环 42 { 43 cycle = new Stack<Integer>(); 44 for (int x = v; x != w; x = edgeTo[x]) // 这里 w 指向该环在本次遍历中最早遇到的顶点 45 cycle.push(x); 46 cycle.push(w); 47 cycle.push(v); 48 } 49 } 50 } 51 52 private boolean hasSelfLoop(Graph G) // 自环 53 { 54 for (int v = 0; v < G.V(); v++) 55 { 56 for (int w : G.adj(v)) 57 { 58 if (v == w) 59 { 60 cycle = new Stack<Integer>(); 61 cycle.push(v); 62 cycle.push(v); 63 return true; 64 } 65 } 66 } 67 return false; 68 } 69 70 private boolean hasParallelEdges(Graph G) // 平行边 71 { 72 marked = new boolean[G.V()]; 73 for (int v = 0; v < G.V(); v++) 74 { 75 for (int w : G.adj(v)) 76 { 77 if (marked[w]) 78 { 79 cycle = new Stack<Integer>(); 80 cycle.push(v); 81 cycle.push(w); 82 cycle.push(v); 83 return true; 84 } 85 marked[w] = true; 86 } 87 for (int w : G.adj(v)) // 恢复遍历前的状态,一遍其他顶点进行检查 88 marked[w] = false; 89 } 90 return false; 91 } 92 93 public boolean hasCycle() 94 { 95 return cycle != null; 96 } 97 98 public Iterable<Integer> cycle() // 将环用迭代器方式输出 99 { 100 return cycle; 101 } 102 103 public static void main(String[] args) 104 { 105 In in = new In(args[0]); 106 Graph G = new Graph(in); 107 class01 finder = new class01(G); 108 if (finder.hasCycle()) 109 { 110 for (int v : finder.cycle()) 111 StdOut.print(v + " "); 112 StdOut.println(); 113 } 114 else 115 StdOut.println(" <main> Graph is aycyclic. "); 116 } 117 }
● 有向图中找环
1 package package01; 2 3 import edu.princeton.cs.algs4.In; 4 import edu.princeton.cs.algs4.StdOut; 5 import edu.princeton.cs.algs4.Digraph; 6 import edu.princeton.cs.algs4.Stack; 7 8 public class class01 9 { 10 private boolean[] marked; 11 private int[] edgeTo; 12 private boolean[] onStack; // 记录递归顺序,递归回退时需要恢复(marked 不恢复) 13 private Stack<Integer> cycle; 14 15 public class01(Digraph G) 16 { 17 marked = new boolean[G.V()]; 18 edgeTo = new int[G.V()]; 19 onStack = new boolean[G.V()]; 20 for (int v = 0; v < G.V(); v++) 21 { 22 if (!marked[v] && cycle == null) 23 dfs(G, v); 24 } 25 } 26 27 private void dfs(Digraph G, int v) // 有向图不用传递起点的父顶点 28 { 29 onStack[v] = true; // 进入新节点时两个变量都要记录 30 marked[v] = true; 31 for (int w : G.adj(v)) 32 { 33 if (cycle != null) 34 return; 35 if (!marked[w]) 36 { 37 edgeTo[w] = v; 38 dfs(G, w); 39 } 40 else if (onStack[w]) // 用 onStack 来检测本次递归是否已经遍历过顶点 w 41 { 42 cycle = new Stack<Integer>(); 43 for (int x = v; x != w; x = edgeTo[x]) 44 cycle.push(x); 45 cycle.push(w); 46 cycle.push(v); 47 } 48 } 49 onStack[v] = false; // 递归回退,恢复搜索痕迹 50 } 51 52 public boolean hasCycle() 53 { 54 return cycle != null; 55 } 56 57 public Iterable<Integer> cycle() 58 { 59 return cycle; 60 } 61 62 public static void main(String[] args) 63 { 64 In in = new In(args[0]); 65 Digraph G = new Digraph(in); 66 class01 finder = new class01(G); 67 if (finder.hasCycle()) 68 { 69 for (int v : finder.cycle()) 70 StdOut.print(v + " "); 71 StdOut.println(); 72 } 73 else 74 StdOut.println(" <main> Graph is aycyclic. "); 75 } 76 }
● 有向图中找环,广度优先搜索,非递归
1 package package01; 2 3 import edu.princeton.cs.algs4.StdOut; 4 import edu.princeton.cs.algs4.StdRandom; 5 import edu.princeton.cs.algs4.Digraph; 6 import edu.princeton.cs.algs4.DigraphGenerator; 7 import edu.princeton.cs.algs4.Stack; 8 import edu.princeton.cs.algs4.Queue; 9 10 public class class01 11 { 12 private Stack<Integer> cycle; 13 14 public class01(Digraph G) 15 { 16 int[] indegree = new int[G.V()]; 17 for (int v = 0; v < G.V(); v++) 18 indegree[v] = G.indegree(v); 19 Queue<Integer> queue = new Queue<Integer>(); 20 for (int v = 0; v < G.V(); v++) // 所有入度为 0 的点入队,可作为遍历的起点,且绝对不是环的顶点 21 { 22 if (indegree[v] == 0) 23 queue.enqueue(v); 24 } 25 for (; !queue.isEmpty();) // 广度优先遍历,反复将 “入度为 0 的顶点的相邻顶点” 的入度减 1,相当于砍掉所有的链 26 { 27 int v = queue.dequeue(); 28 for (int w : G.adj(v)) 29 { 30 indegree[w]--; 31 if (indegree[w] == 0) 32 queue.enqueue(w); 33 } 34 } 35 int[] edgeTo = new int[G.V()]; // 父顶点数组是局部变量就够了 36 int root = -1; // 初始化为非顶点的编号 37 for (int v = 0; v < G.V(); v++) // 遍历顶点,如果还有顶点有入度,说明存在环,将其顶点赋给 root 38 { 39 if (indegree[v] == 0) // 跳过链中的顶点 40 continue; 41 else 42 root = v; 43 for (int w : G.adj(v)) // 顺着入度仍大于 0 的顶点往前爬 44 { 45 if (indegree[w] > 0) 46 edgeTo[w] = v; 47 } 48 } 49 if (root != -1) // root 被覆盖,说明存在环,root 值为最后一个换的最后一个顶点 50 { 51 cycle = new Stack<Integer>(); 52 //for (boolean[] visited = new boolean[G.V()]; !visited[root]; visited[root] = true, root = edgeTo[root]); 53 // 源码中维护了最后一个环的顶点集,但是以后再也没有用到,删掉 54 cycle.push(root); 55 for (int v = edgeTo[root]; v != root; v = edgeTo[v]) 56 cycle.push(v); 57 //cycle.push(root); 多压一次根节点 58 } 59 } 60 61 public boolean hasCycle() 62 { 63 return cycle != null; 64 } 65 66 public Iterable<Integer> cycle() 67 { 68 return cycle; 69 } 70 71 public static void main(String[] args) 72 { 73 int V = Integer.parseInt(args[0]); // 生成 DAG G(V,E),然后再添上 F 条边 74 int E = Integer.parseInt(args[1]); 75 int F = Integer.parseInt(args[2]); 76 Digraph G = DigraphGenerator.dag(V, E); 77 for (int i = 0; i < F; i++) 78 { 79 int v = StdRandom.uniform(V); 80 int w = StdRandom.uniform(V); 81 G.addEdge(v, w); 82 } 83 StdOut.println(G); 84 class01 finder = new class01(G); 85 if (finder.hasCycle()) 86 { 87 for (int v : finder.cycle()) 88 StdOut.print(v + " "); 89 StdOut.println(); 90 } 91 else 92 StdOut.println(" <main> Graph is aycyclic. "); 93 } 94 }