链表带环问题

简介: 链表带环问题

目录

判断链表是否有环

验证fast和slow是否会相遇

求环的长度

返回链表开始入环的第一个节点

判断链表是否有环


这是一道OJ题:环形链表


我们需要判断一个链表是否带环,带环的链表如下图:



c809f51dcd984f209f06a6dafcdd10e3.png



可以看出,我们绝不可以去遍历一天带环链表,如果遍历一个循环链表,程序就会死循环


解决思路如下:

定义slow和fast指针,从头开始走,slow指针每次走1步,fast指针每次走2步


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

1

2

这时的情况如下图:



30bf69252c8d42b78c4e42e58771fe1c.png



然后指针开始运动,fast入环时,slow一定还没入环



09056b328c684200a009d2c49203bb38.png




然后继续运动,slow入环时:



9237b2d0ea264d6dac3f0e8aca6c256b.png




继续运动,fast再次入环,这时在环中,slow在前,fast在后,fast追slow





1d53cbd726ec47abb2729c3f1b0c850c.png



最后,fast追上slow,fast == slow





bc36bd77834e48ffb70d7cea5b3d8193.png



所以代码如下:

bool hasCycle(struct ListNode *head) {
    struct ListNode *fast = head;
    struct ListNode *slow = head;
    while(fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast)
        {
            return true;
        }
    }
    return false;
}


那为什么slow一次走一步,fast一次走两步,就一定能相遇呢?

下面我们来验证一下


验证fast和slow是否会相遇


slow一次走一步,fast一次走两步,它们是否会相遇,是否会错过?


首先,两个指针从头开始运动,首先fast入环,后来slow也入环,此时,假设fast和slow之间的距离为N




70d1b2c3f87e424c9e3e137e69a32246.png



slow进环以后,fast开始追击slow,slow一次走一步,fast一次走两步,它们之间的相对距离每次都减1





ad5bafa97b914cc09367fd8c9180b4c8.png



说以slow一次走一步,fast一次走两步,一定能追上

也说明只要slow和fast之间的距离每次减1,就一定能追上


那么slow一次走1步,fast一次走x(x>=3)步,fast和slow会相遇吗?

下面来验证:


我们以slow一次走1步,fast一次走3步为例

这样它们之间的距离每次减2




2d4b9c2a78874593b9eb798bb653707f.png




这样,就会有2种情况:如果N为偶数,那么就会相遇,如果N为奇数,指针还需要继续运动


那fast和slow距离为-1是怎么导致的呢?

slow走一步,fast走3步,fast从后面超过slow,但是slow == fast没有发生,所以这时的距离就为-1

这时,slow和fast的距离实际上为环的长efb6223b67414bd9a4d56048b0be9144.png度-1






24004115149b40d0bce9da8a418469d8.png8f1ce6a196f14ef495e0c43d56e2f022.png



接下来我们看看slow一次走1步,fast一次走4步



求环的长度f414da7d236b47d0a8d3f025f8ac60ef.png

在前面我们已经可以验证链表是否带环,同时也找到了slow和fast相遇的位置


所以想求环的长度,只需从相遇点开始,让slow再走一圈,设置计数器,就可以得到环的长度


int CycleSize(struct ListNode* head)
{
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    int size = 0;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
        {
            struct ListNode* meet = slow;
            do
            {
                slow = slow->next;
                size++;
            } while (slow != meet);
        }
    }
    return size;
}

表开始入环的第一个节点

这也是一道OJ题,链接:环形链表 II


做这道之前我们要证明一个结论




所以就可以根据这个结论,写出代码

先找到相遇点,然后让一个指针从起始点出发,一个指针从相遇点出发,最后返回这两个指针的相遇点


struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *fast,*slow;
    fast = slow = head;
    while(fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast)
        {
            struct ListNode * meet = slow;
            struct ListNode * start = head;
            while(meet!=start)
            {
                meet = meet->next;
                start = start->next;
            }
            return meet;
        }
    }
    return NULL;
}


这题还有另一种解法


将相遇点断开,然后用相交链表的解法



struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode *cur1,*cur2;
    cur1 = headA;
    cur2 = headB;
    int lenA = 0,lenB = 0;
    while(cur1->next)
    {
        cur1 = cur1->next;
        lenA++;
    }
    while(cur2->next)
    {
        cur2 = cur2->next;
        lenB++;
    }
    if(cur1!=cur2)
    {
        return NULL;
    }
    int gap = abs(lenA-lenB);
    struct ListNode*longer = headA;
    struct ListNode*shorter = headB;
     if(lenA<lenB)
     {
         longer = headB;
         shorter = headA;
     }
     while(gap--)
     {
         longer = longer->next;
     }
     while(longer!=shorter)
     {
         longer = longer->next;
         shorter = shorter->next;
     }
     return longer;
}
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode *fast,*slow;
    fast = slow = head;
    while(fast&&fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow==fast)
        {
            struct ListNode * meet = slow;
            struct ListNode * st1 = meet->next;
            struct ListNode * st2 = head;
            meet->next = NULL;
            return getIntersectionNode(st1,st2);
        }
    }
    return NULL;
}


目录
相关文章
|
机器学习/深度学习 编译器 测试技术
带环链表 复杂链表 刷题+心得【C语言实现 】
带环链表 复杂链表 刷题+心得【C语言实现 】
50 0
|
6月前
|
算法
【数据结构与算法 刷题系列】求带环链表的入环节点(图文详解)
【数据结构与算法 刷题系列】求带环链表的入环节点(图文详解)
|
C语言
C语言:带你轻松干掉 腾讯笔试大题 带环链表
C语言:带你轻松干掉 腾讯笔试大题 带环链表
82 0
数据结构练级之路【链表带环问题】
数据结构练级之路【链表带环问题】
【数据结构】研究链表带环问题
我们只要判断它们是否相遇了,就能确定是否带环,因为如果没有带环的话,那就不可能相遇,快指针肯定先走出去。
126 0
|
Java 索引
(Java)链表OJ题(环形链表,判断链表是否带环,求入环的第一个节点)
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
(Java)链表OJ题(环形链表,判断链表是否带环,求入环的第一个节点)
阿里面试真题详解:带环链表 II
阿里面试真题详解:带环链表 II
阿里面试真题详解:带环链表 II