zoukankan      html  css  js  c++  java
  • 《算法》第四章部分程序 part 6

    ▶ 书中第四章部分程序,加上自己补充的代码,图的环相关

    ● 无向图中寻找环

      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 }
  • 相关阅读:
    双日历时间段选择控件—daterangepicker(汉化版)
    vue elementui table表格展开行每次只展开一行
    vue-pdf PDF文件预览
    async await
    vuex发送axios请求
    jq调用浏览器下载文件 window.open()
    禁止页面右键、选择、F12操作
    vue 点击一条消息跳转相应的页面且对应相应的大模块和办理状态
    vue-infinite-scroll 滚动加载下一页
    填写流程当前登录人可以填写除自己可填项外还可看到他前面经办人相关填写的内容,且经办人后面的不可见
  • 原文地址:https://www.cnblogs.com/cuancuancuanhao/p/9814416.html
Copyright © 2011-2022 走看看