• 图论中环的判断


    无向图环的判断

    • 并查集判断
      • 如果两个结点父亲相同,并且两个结点之间有边相连,则存在环
    def init();
    def find(int x);//必须进行路径压缩
    def merge(int x, int y);
    if(find(x) == find(y) && G[x][y] != 0){ cycle = true;}
    else merge(x,y);
    
    • DFS判断
      • dfs的过程中如果遇到已经访问过的点,并且这个点不是自己的直接父亲,那么就必然存在环
    public class Cycle {
         private boolean[] marked;
         private boolean hasCycle;
         public Cycle(Graph G) {       
             this.marked = new boolean[G.V()];
             this.hasCycle = false;
             for(int i=1; i<G.V(); i++) {
                  if(!marked[i]) {
                      //默认第一个结点没有父节点
                      dfs(G, i, -1);
                  }
             }
         }
    
         private void dfs(Graph G, int cur, int pre) {
             marked[cur] = true;
             for(Integer nxt : G.adj(cur)) {
                  if(!marked[nxt]) {
                      dfs(G, nxt, cur);
                  }
                  else if(nxt != pre) {
                      this.hasCycle = true;
                  }
             }
         }
         public boolean hasCycle() {return this.hasCycle;}
    
    
    

    有向图环的判断

    • 单纯判断

      • 在dfs的过程中,把dfs经过的结点全部保存在一个栈上(或者直接用布尔数组进行标记),然后每当经过一个结点时,判断当前结点是否在栈上,是则有环
      • 事实上其实质就是,在dfs的过程中又遇到了已经访问过的点,则必然存在环
    • 问题:为什么这里不需要考虑:如果该点已经被访问过并且该点是父亲结点的情况呢?无向图中不是考虑了吗?

      • 这里是有向图,如果从孩子结点能有边返回到父亲结点,那么显然这两者之间存在环。
    • 记录环中的结点

      • 需要用栈记录当前路径经过的结点,并且记录每个结点的父亲结点,也就是说记录一下在当前路径中,当前结点是哪一个结点来的。
      • 如果遇到了一个结点已经被访问过并且在栈上, 那么必然存在一个有向环。从这个结点出发,一直找当前结点的父结点,记录到结果容器里面,直到又遇到这个结点。
      • 注意这个dfs路径记录栈,当某一次dfs要返回的时候(此时已经确定在 这一遍dfs中无法找到环),则沿途回溯取消所有结点在栈上的标记(把布尔数组置为false)
    public class DiCycle {
         private boolean[] marked;//记录结点是否访问过
         private int[] edgeTo;//记录该结点的父节点
         private Stack<Integer> cycle;
         private boolean[] onStack;//记录当前访问路径上的结点
        
         public DiCycle(Digraph G) {
             marked = new boolean[G.V()];
             edgeTo  = new int[G.V()];
             cycle = new Stack<Integer>();
             onStack = new boolean[G.V()];
             for(int i=0; i<G.V(); i++) {
                  if(!marked[i]) {
                      dfs(G, i);
                  }
             }
         }
         private void dfs(Digraph G, int v) {
             // TODO Auto-generated method stub
             marked[v] = true;
             onStack[v] = true;
             for(Integer w : G.adj(v)) {
                  if(this.hasCycle()) return ;
                  else if(!marked[w]) {
                      edgeTo[w] = v;
                      dfs(G , w);
                  }
                  else if(onStack[w]) {
                      for(int x=v; x!=w; x=edgeTo[x]) {
                           cycle.push(x);
                      }
                      cycle.push(w); 
                      cycle.push(v);
                  }
             }
             onStack[v] = false;//回溯时取消其在栈上,相当于清空栈,便于下一次dfs       
         }
    
         public void showCycle() {
             if(!this.hasCycle()) System.out.println("no cycle...");
             Stack<Integer> s = new Stack<Integer>();
             s = (Stack<Integer>) cycle.clone();
             while(!s.isEmpty()) {
                  System.out.println(s.peek());
                  s.pop();
              }
         }
    
    
  • 相关阅读:
    三种方法处理文字中的空格
    text——文本属性大全
    font——文字属性大全
    padding和margin——内边距和外边距
    background——背景属性
    C# 解析excel时,字段内有内容,却读取不到的解决方法
    jqprint 打印分页
    pre标签 首行会自动换行解决方案
    正则表达式 清除所有标签的属性
    针对安卓微信浏览器网页 置顶悬浮框浮动固定 的问题
  • 原文地址:https://www.cnblogs.com/czsharecode/p/10709715.html
Copyright © 2020-2023  润新知