【Leetcode每日一题】面试题 02.08. 环路检测

题目

在这里插入图片描述

思路分析

这道题太经典了。不知道近年的行情怎么样,反正四年前秋招找工作的时候,这道题被问过无数遍。幸亏当时只考察代码怎么写,从来没有面试官问过我:为什么用快慢指针在环里就一定能相遇呢?为什么快指针的速度一定是2呢?3行不行?现在想想,我只适合做一个程序员而不是架构师,就是因为我好像只能根据描述快速实现代码,很少去思考答案背后的东西,这个习惯真是太不好了。

我们尝试回答上面的几个问题。首先,为什么用快慢指针一定能相遇?这个问题比较简单,每次前进快指针都比慢指针多走一个节点,假设环的路径大小为len,那么一定存在一个时刻x,使1节点*x=len,此时快指针追上慢指针。

第二个问题是,快慢指针的速度一定要是2和1吗?我个人觉得,如果题目的要求只是“求链表中是否存在环”,那么只要快指针比慢指针快,无论速度多少都可以。假设快指针速度为a,慢指针速度为b,正好相遇的时刻是x,那么只要满足(a-b)x=n * len就可以,显然这个式子总能找到解。但是如果像这道题一样,要求求出环的开头节点,那么速度用其他值应该比较难求出来。这点看完下面的证明相信应该能感受到。

最后的问题是,这道题如何求解?即使过去了4年,看到这道题我还能想起来解法是:快指针每次移动2个节点,慢指针每次移动1个节点,快慢指针相遇时,新增一个指针从链表开头出发,与慢指针同时移动,每次移动1个节点,当它与慢指针相遇时,所在位置就是环的入口。但是,为什么呢?

我们假设两指针相遇在点A,相遇时假设快指针经过了k圈,慢指针经过了k’圈,那么快指针经过的路程为a+k(b+c)+b,慢指针经过的路程为a+k’(b+c)+b,又因为快指针的路程=慢指针的路程*2,所以有:
a+k(b+c)+b=2(a+k’(b+c)+b)
a=(k-2k’)(b+c)-b
a=(k-2k’)(b+c)-(b+c)+c
a=(k-2k’-1)(b+c)+c
假设这时候一个新指针从起点出发,慢指针从相遇点出发会发生什么呢?新指针从起点走到入环点,经过的路程距离是a,那么根据上面的式子,慢指针此时经过的距离一定是(k-2k’-1)(b+c)+c,也就是说,慢指针经过了k-2k’-1圈环的距离+相遇点再走到入环点的距离c!此时慢指针一定也停在了入环点!多么神奇!
在这里插入图片描述
那么我们再回到那个问题:如果快指针的速度是慢指针的任意倍为什么不可以?假设是这样,那么上面计算出来的公式里b+c前面的系数不一定是整数,因此不可以。有兴趣的朋友可以自行证明。

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        if (slow && slow->next) {
            slow = slow->next;
        } else {
            return NULL;
        }
        if (fast->next->next) {
            fast = fast->next->next;
        } else {
            return NULL;
        }
        while (fast && (fast != slow)) {
            slow = slow->next;
            if (fast->next) {
                fast = fast->next->next;
            } else {
                return NULL;
            }
        }
        if (fast != slow) {
            return NULL;
        }
        ListNode *res = head;
        while (res != slow) {
            res = res->next;
            slow = slow->next;
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值