有向/无向图中搜环

  经常遇到一类问题,提供一个图,判断其中是否含环。所谓的环是一条起点与终点相同的路径(至少含有一条边,两个结点)。由于不带环的连通图和带环的连通图有着本质的区别,不带环的连通图是树,而树相较于一般的图可以支持更多更高效的算法,比如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|)。

原文地址:https://www.cnblogs.com/dalt/p/8401432.html

时间: 2024-10-09 10:41:38

有向/无向图中搜环的相关文章

无向图是否有环

判断无向图中是否存在回路(环)的算法描述 如果存在回路,则必存在一个子图,是一个环路.环路中所有顶点的度>=2. 算法: 第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减一. 第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一. 如果最后还有未删除顶点,则存在环,否则没有环. 算法分析: 由于有m条边,n个顶点.如果m>=n,则根据图论知识可直接判断存在环路. (证明:如果没有环路,则该图必然是k棵树 k>=1.根据树的性质,边的数

并查集(1)-判断无向图是否存在环

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.常常在使用中以森林来表示.集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并. Find:确定元素属于哪一个子集.它可以被用来确定两个元素是否属于同一子集合. Union:将两个子集合并成同一个集合. 其实判断一个图是否存在环已经有相应的算法,此文用并查集来判断一个图是否有环. 我们可以用一个一维数组parent[] 来记录子集合. 看下面这个图: 0 | | 1

hdu4612 无向图中任意添加一条边后使桥的数量最少 / 无向图缩点+求树的直径

题意如上,含有重边(重边的话,俩个点就可以构成了边双连通). 先缩点成树,在求数的直径,最远的连起来,剩下边(桥)的自然最少.这里学习了树的直径求法:第一次选任意起点U,进行bfs,到达最远的一个点v(level最深)该点必然是树的直径的一个端点,,再从该点出发,bfs,到最深的一点,该点深度就是直径.(证明:先假设u,是直径上一点,S,T是直径的端点,设v!=t,则有(V,U)+(U,S)>(T,U)+(U,S),矛盾,故t=v:若u不是直径上一点,设u到直径上的一点为x,同理易证. 最后 缩

求有环单链表中的环长、环起点、链表长

1.判断单链表是否有环 使用两个slow, fast指针从头开始扫描链表.指针slow 每次走1步,指针fast每次走2步.如果存在环,则指针slow.fast会相遇:如果不存在环,指针fast遇到NULL退出. 就是所谓的追击相遇问题: 2.求有环单链表的环长 在环上相遇后,记录第一次相遇点为Pos,之后指针slow继续每次走1步,fast每次走2步.在下次相遇的时候fast比slow正好又多走了一圈,也就是多走的距离等于环长. 设从第一次相遇到第二次相遇,设slow走了len步,则fast走

[CareerCup] 2.6 Linked List Cycle 单链表中的环

2.6 Given a circular linked list, implement an algorithm which returns the node at the beginning of the loop.DEFINITIONCircular linked list: A (corrupt) linked list in which a node's next pointer points to an earlier node, so as to make a loop in the

Tree Operations 打印出有向图中的环

题目: You are given a binary tree with unique integer values on each node. However, the child pointers on each node may point to any other node in the tree including itself, introducing cycles into the binary tree. A cycle is defined when you can trave

第九十题(1.不开辟暂时空间交换 2.删除串中指定字符 3.推断链表中存在环)

1.不开辟用于交换数据的暂时空间,怎样完毕字符串的逆序 2.删除串中指定的字符 3.推断单链表中是否存在环 分析和代码: 1,不开辟用于交换的暂时空间,能够用异或交换.或者用字符串的'\0'位置的空间(打个擦边球,使用已有空间.不算开辟). void switch1(char* str) //使用异或交换 { int len = strlen(str); for (int i = 0; i < len / 2; i++) str[i] ^= str[len - i - 1] ^= str[i]

LA 3353 Optimal Bus Route Design 二分匹配和有向图中的环

题意:题目给出一个有向图 , 找若干个圈,使得每个结点切好属于一个圈,并且所有圈的总长度最小 , 如果没有满足条件的就输出 'N' . 注意:1.有重边 2.如果有向边(u , v) , (v , u)都存在 , 它们的长度不一定相同. 解法: 刚看这个题目的时候,没有什么思路,知道是用二分匹配之后就更没思路了.这题的关键还是在于构图: 每个点分成入度点和出度点两个点,然后在从新建图,例如:u 分成 u1 , u2 , v 分成 v1 , v2 , 假如有 (u , v) 这条边 , 那么就变成

[LeetCode] Linked List Cycle II 单链表中的环之二

Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Follow up: Can you solve it without using extra space? 这个求单链表中的环的起始点是之前那个判断单链表中是否有环的延伸,可参见我之前的一篇文章 (http://www.cnblogs.com/grandyang/p/4137187.html). 还是要设