【数据结构题目】环形链表及其背后的数理问题(讲透!!!)

前言
环形链表问题的代码实现都很简单,但是背后的本质,也就是相关的数理问题,还是有一定难度的,面试官如果考察环形链表问题,一定会涉及到背后的数理问题,所以请读者多多重视。

1.环形链表一

LeetCode

1.1 解题方法:快慢指针

创建两个指针,快指针是慢指针移动速度的两倍。两个指针同时从链表的头节点开始移动,如果两个指针相遇了,证明有环,没相遇,就没环。
按照这个思路,代码如下

1.1.1 代码实现

typedef struct ListNode ListNode;

bool hasCycle(struct ListNode *head) 
{
    ListNode* slow = head;
    ListNode* fast = head;

    //快慢指针相遇时,证明有环
    while(fast && fast->next)//链表个数分奇偶
    {
        slow = slow->next;
        fast = fast->next->next;

        //相遇
        if (fast == slow)
        {
            return true;
        }
    }

    //出循环证明没有环
    return false;
}

上面的代码实现过程很简单,并且也能成功AC,但是现在请读者思考:两个指针一定会相遇吗?可不可能刚好错开呢?如果快指针是慢指针速度的三倍呢,还能不能追上呢?四倍、五倍…N倍呢?

1.2 数理分析

1.2.1 快指针速度是慢指针两倍

如果快指针速度是慢指针两倍,则它们的相对速度是一步,那么无论之前他们的差距是多少,都快指针早晚都能一步一步在环形区域追上慢指针。
快慢指针
无论N(距离)是多少,除以1(相对速度)都能除尽,所以一定能刚好相遇。

1.2.2 快指针速度是慢指针三倍

如果快指针速度是慢指针两倍,则它们的相对速度是两步,那么当距离N为偶数时,因为偶数除以二可以除尽,所以可以刚好相遇。但是如果距离N为奇数,那么就会刚好错开,此时我们假设环的周长是C,那么两个指针的距离就变成C-1。
在这里插入图片描述
我们进一步分析,继续按照刚才思路,如果C-1为偶数,那么仍然是可以刚好相遇。可是如果C-1为也是奇数,那就陷入循环了,永远都无法相遇。

我们现在来总结一下:
1.N为偶数,可以刚好相遇;
2.N为奇数,C-1为奇数即C为偶数,会刚好错开,永远无法相遇。
所以结论是快慢指针的确是会错开,这个方法是有缺陷的。

但是请读者再深入思考一下:N和C到底会不会一奇一偶呢?

继续进行数理分析:假设头节点和slow的距离是L,慢指针进环时,快指针已经转了X圈,又已知快指针的路程是慢指针的三倍,可以根据该等量关系列出等式。
在这里插入图片描述
综上所述,N和C不可能一奇一偶,快指针速度是慢指针N倍的情况,也可以通过类似的分析得到同样的结果,只要链表中存在环,快慢指针就一定会相遇

2.环形链表二

环形链表二

2.1 解题过程

这个题目需要先进行数理分析,才能发现一定规律,从而用代码实现。

2.1.1 数理分析

假设头节点到入环第一节点的距离是L,环的长度是C,快指针先转了X圈,快慢指针相遇时候,慢指针运动的距离为S。
同时已知快指针的路程是慢指针路程的两倍,可以根据该等量关系列出等式。
在这里插入图片描述
结论: 所以如果head和meet同时运动,相遇的点就一定是入环的第一个节点。根据此规律实现代码。

2.2.1 代码实现

typedef struct ListNode ListNode;

struct ListNode *detectCycle(struct ListNode *head) 
{
    
    ListNode* slow = head;
    ListNode* fast = head;

    //快慢指针相遇时,证明有环
    while(fast && fast->next)//当链表个数为奇数时,fast为NULL就到了链表结尾
    {                        //为偶数时则fast->next为NULL
        slow = slow->next;
        fast = fast->next->next;

        //相遇
        if (fast == slow)
        {
            ListNode* meet = fast;
            
            //同时移动meet和head,相等时就是入环第一个节点的位置
            while (head != meet)
            {
                meet = meet->next;
                head = head->next;
            }

            return meet;
        }
    }

    //出循环证明没有环
    return NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值