《算法》第四章部分程序 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 }

猜你喜欢

转载自www.cnblogs.com/cuancuancuanhao/p/9824161.html