《演算法》第四章部分程式 part 10
阿新 • • 發佈:2018-11-08
▶ 書中第四章部分程式,包括在加上自己補充的程式碼,包括無向圖連通分量,Kosaraju - Sharir 演算法、Tarjan 演算法、Gabow 演算法計算有向圖的強連通分量
● 無向圖連通分量
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.Queue; 7 importedu.princeton.cs.algs4.EdgeWeightedGraph; 8 import edu.princeton.cs.algs4.Edge; 9 10 public class class01 11 { 12 private boolean[] marked; 13 private int[] id; // 連通分量的標號 14 private int[] size; // 連通分量頂點數 15 private int count; // 連通分量數 16 17 publicclass01(Graph G) 18 { 19 marked = new boolean[G.V()]; 20 id = new int[G.V()]; 21 size = new int[G.V()]; 22 for (int v = 0; v < G.V(); v++) 23 { 24 if (!marked[v]) 25 { 26 dfs(G, v); 27 count++;28 } 29 } 30 } 31 32 public class01(EdgeWeightedGraph G) // 有邊權的圖,演算法相同,資料型別不同 33 { 34 marked = new boolean[G.V()]; 35 id = new int[G.V()]; 36 size = new int[G.V()]; 37 for (int v = 0; v < G.V(); v++) 38 { 39 if (!marked[v]) 40 { 41 dfs(G, v); 42 count++; 43 } 44 } 45 } 46 47 private void dfs(Graph G, int v) 48 { 49 marked[v] = true; 50 id[v] = count; // 深度優先搜尋時,頂點分類,連通分量尺寸更新 51 size[count]++; 52 for (int w : G.adj(v)) 53 { 54 if (!marked[w]) 55 dfs(G, w); 56 } 57 } 58 59 private void dfs(EdgeWeightedGraph G, int v) 60 { 61 marked[v] = true; 62 id[v] = count; 63 size[count]++; 64 for (Edge e : G.adj(v)) 65 { 66 int w = e.other(v); 67 if (!marked[w]) 68 dfs(G, w); 69 } 70 } 71 72 public int id(int v) 73 { 74 return id[v]; 75 } 76 77 public int size(int v) 78 { 79 return size[id[v]]; 80 } 81 82 public int count() 83 { 84 return count; 85 } 86 87 public boolean connected(int v, int w) 88 { 89 return id[v] == id[w]; 90 } 91 92 public static void main(String[] args) 93 { 94 In in = new In(args[0]); 95 Graph G = new Graph(in); 96 class01 cc = new class01(G); 97 int m = cc.count(); 98 Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m]; // 每個連通分量放入一個佇列 99 for (int i = 0; i < m; i++) 100 components[i] = new Queue<Integer>(); 101 for (int v = 0; v < G.V(); v++) 102 components[cc.id(v)].enqueue(v); 103 104 StdOut.println(m + " components"); 105 for (int i = 0; i < m; i++) 106 { 107 for (int v : components[i]) 108 StdOut.print(v + " "); 109 StdOut.println(); 110 } 111 } 112 }
● Kosaraju - Sharir 演算法計算有向圖的強連通分量
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.Queue; 7 import edu.princeton.cs.algs4.DepthFirstOrder; 8 9 public class class01 10 { 11 private boolean[] marked; 12 private int[] id; 13 private int[] size; 14 private int count; 15 16 public class01(Digraph G) 17 { 18 DepthFirstOrder dfs = new DepthFirstOrder(G.reverse()); // 對 G 的逆圖進行深度優先搜尋 19 marked = new boolean[G.V()]; 20 id = new int[G.V()]; 21 size = new int[G.V()]; 22 for (int v : dfs.reversePost()) // 使用 G 逆圖 dfs 序對 G 進行深度優先搜尋 23 { 24 if (!marked[v]) 25 { 26 dfs(G, v); 27 count++; 28 } 29 } 30 } 31 32 private void dfs(Digraph G, int v) 33 { 34 marked[v] = true; 35 id[v] = count; 36 size[count]++; 37 for (int w : G.adj(v)) 38 { 39 if (!marked[w]) 40 dfs(G, w); 41 } 42 } 43 44 public int id(int v) 45 { 46 return id[v]; 47 } 48 49 public int size(int v) 50 { 51 return size[id[v]]; 52 } 53 54 public int count() 55 { 56 return count; 57 } 58 59 public boolean strongConnected(int v, int w) 60 { 61 return id[v] == id[w]; 62 } 63 64 public static void main(String[] args) 65 { 66 In in = new In(args[0]); 67 Digraph G = new Digraph(in); 68 class01 scc = new class01(G); 69 int m = scc.count(); 70 Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m]; 71 for (int i = 0; i < m; i++) 72 components[i] = new Queue<Integer>(); 73 for (int v = 0; v < G.V(); v++) 74 components[scc.id(v)].enqueue(v); 75 76 StdOut.println(m + " components"); 77 for (int i = 0; i < m; i++) 78 { 79 for (int v : components[i]) 80 StdOut.print(v + " "); 81 StdOut.println(); 82 } 83 } 84 }
● Tarjan 演算法計算有向圖的強連通分量
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.Queue; 7 import edu.princeton.cs.algs4.Stack; 8 9 public class class01 10 { 11 private boolean[] marked; 12 private int[] id; 13 private int[] size; 14 private int[] low; // 遍歷序中頂點 v 的最小深度 15 private int pre; // 優先順序(遍歷頂點時不斷維護) 16 private int count; 17 private Stack<Integer> stack; 18 19 public class01(Digraph G) 20 { 21 marked = new boolean[G.V()]; 22 id = new int[G.V()]; 23 size = new int[G.V()]; 24 low = new int[G.V()]; 25 stack = new Stack<Integer>(); 26 for (int v = 0; v < G.V(); v++) // 正常順序深度優先遍歷圖 27 { 28 if (!marked[v]) 29 dfs(G, v); 30 } 31 } 32 33 private void dfs(Digraph G, int v) 34 { 35 marked[v] = true; 36 low[v] = pre++; // 更新遞迴深度 37 int min = low[v]; // 記錄 v 所在的的遞迴深度 38 stack.push(v); 39 for (int w : G.adj(v)) 40 { 41 if (!marked[w]) 42 dfs(G, w); 43 if (low[w] < min) // 尋找 v 鄰居的最小深度,low[w] < min 說明找到了後向邊(v->w 關於棧中的元素構成有向環) 44 min = low[w]; 45 } 46 if (min < low[v]) // 改寫 v 的最小深度,終止遞迴(沿著棧一路改寫回去,直到棧中首次出現有向環的元素為止(再往前的頂點滿足 low[x] < min),進入吐棧環節 47 { 48 low[v] = min; 49 return; 50 } 51 int sizeTemp = 0; 52 for (int w = -1; w != v; sizeTemp++) // 從棧頂吐到 v 為止,都是一個強連通分量,這裡 v 是有向環首元再往前一格的頂點 53 { 54 w = stack.pop(); 55 id[w] = count; // 標記吐出的強連通分量 56 low[w] = G.V(); // 將已經記錄了的連通分量的深度改為圖的頂點數(遞迴可能的最大深度) 57 } 58 size[count] = sizeTemp; 59 count++; // 增加連通分量計數 60 } 61 62 public int id(int v) 63 { 64 return id[v]; 65 } 66 67 public int size(int v) 68 { 69 return size[id[v]]; 70 } 71 72 public int count() 73 { 74 return count; 75 } 76 77 public boolean strongConnected(int v, int w) 78 { 79 return id[v] == id[w]; 80 } 81 82 public static void main(String[] args) 83 { 84 In in = new In(args[0]); 85 Digraph G = new Digraph(in); 86 class01 scc = new class01(G); 87 int m = scc.count(); 88 Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m]; 89 for (int i = 0; i < m; i++) 90 components[i] = new Queue<Integer>(); 91 for (int v = 0; v < G.V(); v++) 92 components[scc.id(v)].enqueue(v); 93 94 StdOut.println(m + " components"); 95 for (int i = 0; i < m; i++) 96 { 97 for (int v : components[i]) 98 StdOut.print(v + " "); 99 StdOut.println(); 100 } 101 } 102 }
● Gabow 演算法計算有向圖的強連通分量
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.Queue; 7 import edu.princeton.cs.algs4.Stack; 8 9 public class class01 10 { 11 private boolean[] marked; 12 private int[] id; 13 private int[] size; 14 private int[] preOrder; // 記錄每個頂點的遍歷深度 15 private int pre; 16 private int count; 17 private Stack<Integer> stack1; 18 private Stack<Integer> stack2; // 用棧來代替 Tarjan 演算法中的 low 陣列 19 20 public class01(Digraph G) 21 { 22 marked = new boolean[G.V()]; 23 id = new int[G.V()]; 24 size = new int[G.V()]; 25 preOrder = new int[G.V()]; 26 stack1 = new Stack<Integer>(); 27 stack2 = new Stack<Integer>(); 28 for (int v = 0; v < G.V(); v++) 29 id[v] = -1; 30 for (int v = 0; v < G.V(); v++) 31 { 32 if (!marked[v]) 33 dfs(G, v); 34 } 35 } 36 37 private void dfs(Digraph G, int v) 38 { 39 marked[v] = true; 40 preOrder[v] = pre++; // 更新遞迴深度,一旦寫入就不改變了 41 stack1.push(v); // 同時壓兩個棧 42 stack2.push(v); 43 for (int w : G.adj(v)) 44 { 45 if (!marked[w]) 46 dfs(G, w); 47 else if (id[w] == -1) // 已經遍歷過 w,且 w 不屬於任何連通分量 48 for (; preOrder[stack2.peek()] > preOrder[w]; stack2.pop()); // 把 stack2 中深度大於 w 的頂點全部吐掉(直到棧頂等於有向環的首個元素為止) 49 } 50 if (stack2.peek() == v) // 該式在遞歸回退到棧中首次出現有向環元素的那層時成立,此時 stack2 頂為有向環首元,stack1 頂為有向環末元 51 { // 注意此時 stack2 頂層下(stack1 和 stack2 共有且相等)可能還有其他元素,是不屬於非有向環部分的遍歷路徑 52 stack2.pop(); // stack2 退到首元的上一個元 53 int sizeTemp = 0; 54 for (int w = -1; w != v; sizeTemp++) // 同樣的方法從 stack1 中逐漸吐棧,計算連通分量元素 55 { 56 w = stack1.pop(); 57 id[w] = count; 58 } 59 size[count] = sizeTemp; 60 count++; 61 } 62 } 63 64 public int id(int v) 65 { 66 return id[v]; 67 } 68 69 public int size(int v) 70 { 71 return size[id[v]]; 72 } 73 74 public int count() 75 { 76 return count; 77 } 78 79 public boolean strongConnected(int v, int w) 80 { 81 return id[v] == id[w]; 82 } 83 84 public static void main(String[] args) 85 { 86 In in = new In(args[0]); 87 Digraph G = new Digraph(in); 88 class01 scc = new class01(G); 89 int m = scc.count(); 90 Queue<Integer>[] components = (Queue<Integer>[]) new Queue[m]; 91 for (int i = 0; i < m; i++) 92 components[i] = new Queue<Integer>(); 93 for (int v = 0; v < G.V(); v++) 94 components[scc.id(v)].enqueue(v); 95 96 StdOut.println(m + " components"); 97 for (int i = 0; i < m; i++) 98 { 99 for (int v : components[i]) 100 StdOut.print(v + " "); 101 StdOut.println(); 102 } 103 } 104 }