数据结构入门之链表带环的判定问题引发的思考

一、问题分析        

        先给出题目链接141. 环形链表 - 力扣(LeetCode)        给出思路快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,若链表带环则两指针一定会在环里相遇,即小学中学中的追及问题。

        我们给出实现代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
bool hasCycle(struct ListNode *head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            return true;
        }
    }
    return false;
}

二、提出新疑问

        本题本身不难,但是对思路我们进行扩展提出两个问题:

(一)为什么快指针每次走两步,慢指针走一步可以判定链表带环?

        这个问题不难想到答案,当指针进环时,每走一步相当于快指针从后面追慢指针一步,距离终将缩小为0,即追上。

(二)快指针一次走3步,走4步,...,走n步可以吗?

        我们先判断快指针一次走3步是否可行:

        先假设当slow进环时,fast在这个位置,fast追上slow的距离为L,fast和slow每走一步相当于L-2,当L为偶数时,则能追上;L为奇数时,fast第一次将超过slow一步而不与slow相遇,如果L+N为奇数那此时fast距离追上slow有L+N-1步(偶数),所以可以追上,若L+N为偶数,则永远不能追上。那么现在,似乎可以推导fast一次走4步也分情况可以实现,所以只有fast一次走两步才能成功。

        事实真的如此吗?如果当L为奇数时L+N不可能为偶数,那一次走3步就是可以实现的了。

我们把数学公式写出来:

                        ​​​​​​​        ​​​​​​​        ​​​​​​​        3S=S+n*\left ( L+N \right )+N

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                ​​​​​​​   2S=n*\left ( L+N \right )+N

        假设在slow进环时,fast已经在环里转了n圈,,根据上述等式容易发现问题,当N为奇数时,因为2S为偶数,所以L+N必定为奇数,所以一次走3步也是可行的。后续的推导思路与走3步一致。

三、新问题

        我们根据之前的用数学等式推导的方法,解决新的问题142. 环形链表 II - 力扣(LeetCode)

        返回入环的第一个结点,那我们要分析当fast与slow相遇时的位置,因为此时才能判定确实有环(slow走一步fast走两步):

        相遇时slow走的路程为:S+N;fast走的路程为L+n*(L+N)+N。(因为当fast追上slow时,n不可能等于0),此时有:

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        2\left ( S+N \right ) = S+n*\left ( L+N \right )+N

        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​                S=\left ( n-1 \right )*\left ( L+N \right )+L

        所以相遇位置再走S就能到入环第一个结点(因为S = L+整数倍环长度),所以让一个指针从meet位置往后走,一个指针从head位置往后走,当两指针相遇时,就为入环第一个结点,代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)
        {
            struct ListNode* meet = slow;
            while(meet != head)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值