寻找带环的链表的柄长

试题:
给定一个带环的链表,找出环起点。
比如:A -> B -> C -> D -> E -> C (C为环形起点)
写一个程序找出环起点C。

ListNode结构如下,请实现 ListNode* find_circle_beginning(ListNode* head);函数,返回环的起点。

struct ListNode
{
    char val;
    ListNode* next;
};

答案:

1、先用快指针(每次走两步)和慢指针(每次走一步),遍历链表,当两个指针相遇时,说明该链表存在环。
2、当两个指针遇到时,慢指针退回到链表起点,快指针保留在相遇点,两个指针都以一次一步前进,再次相遇的点就是环的起点。

源码如下:

ListNode* find_circle_beginning(ListNode* head)
{
    if( head == NULL )
    {
        return NULL;
    }

    ListNode* slow = head;
    ListNode* fast = head;

    // 寻找相遇点
    while( slow != NULL && fast != NULL && fast->next != NULL )
    {
        slow = slow->next;
        fast = fast->next->next;
        if( fast == slow )
        {
            // 在此相遇,说明存在环
            break;
        }
    }

    // 如果两个指针不等,说明不存在环
    if( fast != slow )
    {
        return NULL;
    }

    /*
        慢指针退回到链表起点,快指针保留在相遇点,两个指针都以一次一步前进,再次相遇的点就是环的起点。
        稍后会给出证明
    */
    slow = head;
    while( slow != fast )
    {
        slow = slow->next;
        fast = fast->next;
    }

    return fast;
}

证明:

假设环柄长度AB为h,环的长度为p,相遇点为C.

当快指针和慢指针相遇时,慢指针在环上走了m步,总共就是h+m,快指针走过的长度就是2(h+m),在环上走过的长度就是h+2m。在相遇点相遇时,存在以下关系:
(h+2m)%p = m%p
==> ((h+m)+m)%p = m%p
==> (h+m)%p = 0
==> h%p + m%p = p      式(1)

假设BC的长度是m1,则有m%p = m1,带入式(1)得到:
h%p = p - m1
==> h = (p - m1) + ap (a >= 0)

当第二次相遇时,慢指针从头走过h,快指针走(p-m1)+ap,然后在环起点相遇。

小结:

本文的证明实际是倒推的结果。该方法实际上是叫做Pollard‘s Rho Method,详见http://www.csh.rit.edu/~pat/math/quickies/rho/

寻找带环的链表的柄长

时间: 2024-08-28 12:40:28

寻找带环的链表的柄长的相关文章

1.6带环单链表

检测单链表中是否有环 方法一:蛮力法 定义一个集合用来存放结点的引用,并将其初始化为空,从链表的头结点开始向后遍历,每遍历到一个结点就判断集合中是否有这个结点的引用,如果没有,说明这个结点是第一次访问,还没有形成环,那么将这个结点的引用添加到集合中去.如果在集合中找到了同样的结点,那么说明这个结点已经被访问过了,于是就形成了环.这种方法的时间复杂度为O(n),空间复杂度也为O(n). 方法二 : 快慢指针遍历法 定义两个指针fast(快)与slow(慢),二者的初始值都指向链表头,指针slow每

带环单链表

判断单链表是否带环: 定义两个快慢指针,快指针每次走两步,慢指针每次走一步,然后判断是否两个指针相遇.若相遇,则带环. 设慢指针走过的路程为s,则快指针走过的路程即为2s. 设从环头结点到环的入口点的距离为a. 设从环的入口点到两指针相遇点的距离为x. 设环的长度为m.快指针走了n圈. 由数学关系式可得:s = a + x    2s = a + n*m + x; 则  n*m = a + x; a = n*m - x; 求环的入口点方法:定义连个指针,一个指针从环的头结点开始走,另一个指针则从

<笔试><面试>C/C++单链表相关(4)判断两链表是否相交,求交点(链表不带环/可能带环)

判断两链表是否相交,求交点(假设链表不带环) 判断两链表是否相交,求交点(假设链表可能带环) RingEntry_Point()等函数见前篇. SListNode* Intersect(SListNode *&L, SListNode *&M)//判断两链表是否相交,求交点(假设链表不带环) {  //思路:若不带环,只有相交/不想交两种情况  // 与RingEntry_Point()函数方法相同:  //     求两个链表长度之差K,再令一个指针从长链表开始先走K步,令另一个指针从短

判断两个链表是否相交(带环)

解决方案: 1.找出链表1的环入口节点a1,链表2的环入口节点a2; 2.如果a1=a2; 说明两个链表可能在入环之前或者入环第一个节点相交:将a1,a2作为两个链表的最后一个节点,转化为不带环的链表相交:其实在这种情况下已经说明两个链表已经相交了. 3.如果a1!=a2;以a1为基准节点进行while循环,如果在循环中找到跟a2相同的节点,说明两个链表相交:如果没找到,说明不相交. #如果1个链表不带环,1个链表带环:则两个链表必不相交:

[PHP] 算法-请找出带环链表的环的入口结点的PHP实现

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null 1.找链表倒数第k个结点,输入一个链表,输出该链表中倒数第k个结点.第一个指针走(k-1)步,到达第k个节点,两个指针同时往后移动,当第一个结点到达末尾的时候,第二个结点所在位置就是倒数第k个节点了 2.原理有点像上面的,定义两个指针,一个是快指针每次走两步,一个是慢指针每次走一步,当两个相遇的时候,假设环的长度为n个结点,慢指针走x步,快指针走2x步,2x=x+kn ;x=kn; k暂时假定为1圈 ,也就是慢指针slow

求单链表是否带环,和环长

//带环返回1 //不带环返回0 int IsCycle(PLinkList pList, PLinkList*ppMeetNode) { Node*fast = pList; Node*slow = pList; while (fast&&fast->next) { slow = slow->next; fast = fast->next->next; if (slow == fast) { *ppMeetNode = slow; return 1; } } *p

C语言强化(七)链表相交问题_3 判断链表是否带环

前两篇讨论的前提都是链表是无环的,但是如果链表有环呢? 显然,如果链表有环,那么之前的寻找链表尾结点的函数将陷入死循环,之前的算法也将崩掉. 所以对于链表相交的问题,首先要判断的是链表是否有环. 题目 给出俩个单向链表的头指针,比如 h1,h2,判断这俩个链表是否相交 解题步骤 判断两个[无环]链表是否相交 找到两个[无环]链表的相交结点 判断链表是否带环 判断两个[有环]链表是否相交 找到两个[有环]链表的相交结点 思路 用两个指针,一个指针步长为1,一个指针步长为2,若最后相遇,则链表有环

[LintCode] Linked List Cycle(带环链表)

描述 给定一个链表,判断它是否有环. 样例 给出 -21->10->4->5, tail connects to node index 1,返回 true. 这里解释下,题目的意思,在英文原题中,tail connects to node index 1 表示的是节点 5 还要链接回索引号 为 1 的节点. 一个典型的带环链表如下: 挑战 不要使用额外的空间 代码 GitHub 的源代码,请访问下面的链接: https://github.com/cwiki-us/java-tutoria

关于相交链表、带环链表、链表深拷贝的思路整理

返回相交链表的交点:1.先求出两个链表的各自长度2.让长的先走他们的(长度差)步3.然后两者同时走,第一次相遇就是交点(返回该结点) 判断链表是否带环:1.快慢指针(快的走两步,慢的走一步,不能一个一步,一个n步(N>2),可能会错过)2.如果两个指针相遇,则链表带环:如果快的遇到null,则不带环(直线形) 求入环点:1).转化为相交问题(求取相遇结点)2).一个从起点,一个从交点,都每次走一步,第一次相遇点为入环点 相交+带环(六种情况) 复杂链表的复制1)简单复制无法解决(因为是浅拷贝)2