zoukankan      html  css  js  c++  java
  • 判断单链表是否有环

    题目:如何判断链表里面是否有环?

    方法一:快慢指针法

    设两个工作指针,一个快一个慢,如果有环的话,它们会必然在某点相遇。

    为什么当单链表存在环时,p和q一定会相遇呢?


    假定单链表的长度为n,并且该单链表是环状的,那么第i次迭代时,p指向元素i mod n,q指向2i mod n。因此当i≡2i(mod n)时,p与q相遇。而i≡2i(mod n) => (2i - i) mod n = 0 => i mod n = 0 => 当i=n时,p与q相遇。这里一个简单的理解是,p和q同时在操场跑步,其中q的速度是p的两倍,当他们两个同时出发时,p跑一圈到达起点,而q此时也刚好跑完两圈到达起点。
    那么当p与q起点不同呢?假定第i次迭代时p指向元素i mod n,q指向k+2i mod n,其中0<k<n。那么i≡(2i+k)(mod n) => (i+k) mod n = 0 => 当i=n-k时,p与q相遇。

    扩展:

    1. 如果两个指针的速度不一样,比如p,q,( 0<p<q)二者满足什么样的关系,可以使得两者肯定交与一个节点?

        Sp(i) = pi

        Sq(i) = k + qi

       如果两个要相交于一个节点,则 Sp(i) = Sq(i) =>  (pi) mod n = ( k+ qi ) mod n =>[ (q -p)i + k ]  mod n =0

       =>  (q-p)i + k  = Nn [N 为自然数]

       =>  i = (Nn -k) /(p-q)

       i取自然数,则当 p,q满足上面等式 即 存在一个自然数N,可以满足Nn -k 是 p - q 的倍数时,保证两者相交。

       特例:如果q 是p 的步长的两倍,都从同一个起点开始,即 q = 2p , k =0, 那么等式变为: Nn=i: 即可以理解为,当第i次迭代时,i是圈的整数倍时,两者都可以交,交点就是为起点。

    2.如何判断单链表的环的长度?

    记录下问题1的碰撞点p,slow、fast从该点开始,再次碰撞所走过的操作数就是环的长度s。

    3. 如何找到链表中第一个在环里的节点?

       假设链表长度是L,前半部分长度为k-1,那么第一个再环里的节点是k,环的长度是 n, 那么当q=2p时, 什么时候第一次相交呢?当q指针走到第k个节点时,q指针已经在环的第 k mod n 的位置。即p和q 相差k个元素,从不同的起点开始,则相交的位置为 n-k, 则有了下面的图:

    从图上可以明显看到,当p从交点的位置(n-k) ,向前遍历k个节点就到到达环的第一个几点,节点k.

    算法就很简单: 一个指针从p和q 中的第一次相交的位置起(n-k),另外一个指针从链表头开始遍历,其交点就是链表中第一个在环里的交点

    也有大神说这里其实是一个定理:碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。

    实际上和我们上面分析的是一个意思,要理解着来还是背下来就随便你咯,定理证明见:http://blog.sina.com.cn/s/blog_725dd1010100tqwp.html

    4、带环链表的长度是多少?

    问题3中已经求出连接点距离头指针的长度,加上问题2中求出的环的长度,二者之和就是带环单链表的长度

    4. 如果判断两个单链表有交?第一个交点在哪里?

     1 bool IsExitsLoop(slist *head)  
     2 {  
     3     slist *slow = head, *fast = head;  
     4   
     5     while ( fast && fast->next )   
     6     {  
     7         slow = slow->next;  
     8         fast = fast->next->next;  
     9         if ( slow == fast ) break;  
    10     }  
    11   
    12     return !(fast == NULL || fast->next == NULL);  
    13 } 

    找到交点的思路是把其中链表一个链表尾节点与头节点相连,如果有环,则很容发现问题转化为问题3,求有环的链表的第一个在环里的节点。

    找到环点:

     1 slist* FindLoopPort(slist *head)  
     2 {  
     3     slist *slow = head, *fast = head;  
     4   
     5     while ( fast && fast->next )   
     6     {  
     7         slow = slow->next;  
     8         fast = fast->next->next;  
     9         if ( slow == fast ) break;  
    10     }  
    11   
    12     if (fast == NULL || fast->next == NULL)  
    13         return NULL;  
    14   
    15     slow = head;  
    16     while (slow != fast)  
    17     {  
    18          slow = slow->next;  
    19          fast = fast->next;  
    20     }  
    21   
    22     return slow;  
    23 }  

     方法二.

    设两个工作指针p、q,p总是向前走,但q每次都从头开始走,对于每个节点,看p走的步数是否和q一样。比如p从A走到D,用了4步,而q则用了14步。因而步数不等,出现矛盾,存在环.

    //if two pointer are equal, but they don't have the same steps, then has a loop
    02
    int HasLoop(LinkList L)
    03
    {
    04
        LinkList cur1 = L;  // 定义结点 cur1
    05
        int pos1 = 0;       // cur1 的步数
    06
        while(cur1){        // cur1 结点存在
    07
            LinkList cur2 = L;  // 定义结点 cur2
    08
            int pos2 = 0;       // cur2 的步数
    09
            pos1 ++;            // cur1 步数自增
    10
            while(cur2){        // cur2 结点不为空
    11
                pos2 ++;        // cur2 步数自增
    12
                if(cur2 == cur1){   // 当cur1与cur2到达相同结点时
    13
                    if(pos1 == pos2)    // 走过的步数一样
    14
                        break;          // 说明没有还
    15
                    else                // 否则
    16
                        return 1;       // 有环并返回1
    17
                }
    18
                cur2 = cur2->next;      //  如果没发现环,继续下一个结点
    19
            }
    20
            cur1 = cur1->next;  // cur1继续向后一个结点
    21
        }
    22
        return 0;
    23
    }

    方法三

    在环的入口点出断开,从而转换为看两个链表是否有交点的问题(改日补充)

    参考资料:http://blog.csdn.net/yiwuxue/article/details/21973079

           http://blog.csdn.net/cuit/article/details/35365219

     

  • 相关阅读:
    HDU 5938 Four Operations 【贪心】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5935 Car 【模拟】 (2016年中国大学生程序设计竞赛(杭州))
    HDU 5934 Bomb 【图论缩点】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5933 ArcSoft's Office Rearrangement 【模拟】(2016年中国大学生程序设计竞赛(杭州))
    HDU 5929 Basic Data Structure 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    【转】LaTeX 符号命令大全
    HDU 5922 Minimum’s Revenge 【模拟】 (2016CCPC东北地区大学生程序设计竞赛)
    HDU 5927 Auxiliary Set 【DFS+树】(2016CCPC东北地区大学生程序设计竞赛)
    数据结构之稀疏矩阵
    C++中引用(&)的用法和应用实例
  • 原文地址:https://www.cnblogs.com/curo0119/p/8330846.html
Copyright © 2011-2022 走看看