zoukankan      html  css  js  c++  java
  • 有向/无向图中搜环

      经常遇到一类问题,提供一个图,判断其中是否含环。所谓的环是一条起点与终点相同的路径(至少含有一条边,两个结点)。由于不带环的连通图和带环的连通图有着本质的区别,不带环的连通图是树,而树相较于一般的图可以支持更多更高效的算法,比如log2(n)时间复杂度内找任意两点的路径信息,在树上进行树形DP等等。

      图按照边是否有向可以分为有向图和无向图。在两类图中找环的时间复杂度均为O(n),而判断是否含环的时间复杂度也是O(n),因此只陈述找环的方法。

    无向图找环

      无向图找环,因为无向图中没有明确的根,我们可以令任意结点为根做DFS操作,每次搜索到一个结点,就修改其状态为已访问,直到搜索到已访问的结点u,这时候通过退栈必定会碰到另外一个u结点,二者之间的路径就是环。

    findLoop(node, father, stack) //node为根结点,father设为空,stack用于记录可能存在的环信息
        stack.push(node)
        if(node.visit)
            return true
        node.visit = true
        for child in node.children
            if(child == father)
                continue
            if(findLoop(child, node, stack))
                return true
        stack.pop()
        return false

      说明正确性。很显然如果我们第二次访问某个结点,很显然两次访问对应的路径必定有相同的根结点(因为无向图的原因DFS会搜索整个连通图中的所有结点),由于DFS每次得到的路径不同,因此我们得到了两条起点相同终点相同的路径,将两条路径首位相连,我们就得到了一个环。如果我们第二次访问相同结点u,那么当前路径中必定包含u,因为在第一次搜索到u时,我们会继续搜索其子树,此时沿着第二条路径逆向走,必定会抵达某个访问过的结点(可能是根),那么若这个结点不是u,就违背了u是第一个被二次访问结点这一前提,故当前路径中必定包含u,两个结点之间的路径中没有重复结点,是一个简单环。

      复杂度非常简单,我们为每个未被访问过的结点调用该方法,每次进入方法,或者修改结点的访问状态,或者找到环,而函数内部的逻辑(不含循环)是常数时间复杂度,循环最多发生|E|次,因此时间复杂度为O(|V|+|E|)。

    有向图找环

      有向图找环相对比较复杂。由于图未必连通(可能由若干连通子图构成),我们需要建立一个公共的根结点r,并从r向图中所有结点建立一条单向边(由于r只有出边,因此r必定不是环的一部分,即不影响我们找到的环)。之后从r出发进行DFS。同样我们需要增加访问状态来避免重复搜索,但是访问两次的结点未必构成环,比如考虑两条路径r->a->b与r->b,很显然a与b之间未必构成环。我们还需要加入一个在栈标志instk,为true表示这个结点在当前路径上,false表示不在。只有搜到的二次访问结点满足该结点在栈中,才能保证其处于环上。  

    findLoop(node, stack)
        if node.visit
            if node.instk == false
                return false
            stack.push(node)
            return true;
        node.visit = true
        node.instk = true
        stack.push(node)
        
        for child in node.children
            if(findLoop(child, stack))
                return true
        
        node.instk = false
        stack.pop()
        return false

      如果图中确实含环,即存在路径u->...->u,那么当我们访问到环上的任意结点u时,由于DFS的缘由,必定会回到自身或是找到另外一个环并退出,无论哪种情况我们都找到了一个环。而如果第一次发现一个结点u被二次访问且在栈中,那么由于该结点在栈中,那么路径中必然包含u,二者之间的路径则形成了环,而由于路径中只含有访问一次的结点(除了u),因此找到的环是简单环。

      时间复杂度与无向图的一致,也是O(|V|+|E|)。

  • 相关阅读:
    LeetCode题解之Flipping an Image
    LeetCode 之Find Minimum in Rotated Sorted Array
    LeetCode题解Transpose Matrix
    LeetCode 题解之Minimum Index Sum of Two Lists
    LeetCode题解之Intersection of Two Linked Lists
    LeetCode 题解之Add Two Numbers II
    LeetCode题解之Add two numbers
    href="#"与href="javascript:void(0)"的区别
    有关ie9 以下不支持placeholder属性以及获得焦点placeholder的移除
    ie7下属性书写不规范造成的easyui 弹窗布局紊乱
  • 原文地址:https://www.cnblogs.com/dalt/p/8401432.html
Copyright © 2011-2022 走看看