• 《算法》第四章部分程序 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 }
  • 相关阅读:
    nyoj 202红黑树 (搜索)
    POJ 3281 Dining(最大流)
    nyoj-488 素数环 +nyoj -32 组合数 (搜索)
    LeetCode100:Same Tree
    LeetCode283:Move Zeros
    Leetcode226:Invert Binary Tree
    LeetCode258:Add Digits
    Leetcode237:Delete Node in a Linked List
    LeetCode7:Reverse Integer
    LeetCode292:Nim Game
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/9824161.html
Copyright © 2020-2023  润新知