1. 程式人生 > >《演算法》第四章部分程式 part 10

《演算法》第四章部分程式 part 10

▶ 書中第四章部分程式,包括在加上自己補充的程式碼,包括無向圖連通分量,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 import
edu.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 public
class01(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 }