[LeetCode] Linked List Cycle 与 Linked List Cycle II

前言

Linked List Cycle 与 Linked List Cycle II是LeetCode链表系列很经典的两道题,值得研究一下。

题目

https://leetcode.com/problems/linked-list-cycle/
https://leetcode.com/problems/linked-list-cycle-ii/

Linked List Cycle

Given a linked list, determine if it has a cycle in it.
Can you solve it without using extra space?

分析

判断链表有没有环,最容易想到的方法是,用一个哈希表来记录每个元素是否被访问过,一旦出现某个元素被重复访问,说明存在环。空间复杂度 O(n),时间复杂度 O(N)。

最好的方法是时间复杂度 O(n),空间复杂度 O(1) 的。我们可以设置两个指针,一个快一个慢,快
的指针每次走两步,慢的指针每次走一步,如果快指针和慢指针相遇,则说明有环。
关于这种“快慢指针法”,还可参看LeetCode官方博客文章:http://articles.leetcode.com/detecting-loop-in-singly-linked-list
另外,对于这种解法的正确性,即“快慢指针若相遇链表保证有环”的正确性,这里引用一个这篇博客文章提到的证明方法:

这里写图片描述

如图,在任意时刻,p1和p2都在环上。由于p1每次向前1步,p2每次向前两步,用相对运动的观点(中学物理)来看,把p1看作静止,那么p2每次相对p1向前1步,二者在顺时针方向上的距离每经过一个时刻就减少1,直到变为0,也即二者恰好相遇。这样就证明了对于有环链表,二者也是必然在某一时刻相遇在某个节点上的。(除非这个环有无穷大,那么p2追上p1可能需要t=∞的时间)

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode * SlowPtr = head, *FastPtr = head;
        while (FastPtr != NULL && FastPtr->next != NULL) {
            SlowPtr = SlowPtr->next;
            FastPtr = FastPtr->next->next;//快指针每次走两步
            if (SlowPtr == FastPtr) 
                return true;
        }
        return false;
    }
};

Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Note: Do not modify the linked list.
Follow up: Can you solve it without using extra space?

分析

题意很简单,如果有环,返回环的入口,如果没有,则返回NULL。
对于有环的链表而言,当 fast 与 slow 相遇时, slow 肯定没有遍历完链表,而 fast 已经在环内循环了 n 圈 (1 ≤ n)。现在假
设 slow 走了 s 步,则 fast 走了 2s 步( 同时,fast 步数还等于 s 加上在环上多转的 n 圈),设环长为 r,则有:

2s = s + nr

因此,

s = nr

设整个链表长 L,环入口点与相遇点距离为 a,起点到环入口点的距离为 x,则

x + a = nr = (n–1)r + r = (n − 1)r + L − x

x = (n − 1)r + (L–x–a)

L–x–a 为相遇点到环入口点的距离,由此可知,从链表头到环入口点等于 n − 1 圈内环 + 相遇
点到环入口点,于是我们可以从 head 开始另设一个指针 slow2,两个慢指针每次前进一步,它俩一定会在环入口点相遇。

另外,刚才那篇文章最后也讨论了这个找环入口的问题,解释地相当清楚明了,一并推荐之。

代码

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode * SlowPtr = head, *FastPtr = head;
        while (FastPtr != NULL && FastPtr->next != NULL) {
            SlowPtr = SlowPtr->next;
            FastPtr = FastPtr->next->next;
            if (SlowPtr == FastPtr) {//存在环
                ListNode *WalkerPtr = head;//重新设置一个慢指针
                while (WalkerPtr != SlowPtr) {
                    WalkerPtr = WalkerPtr->next;
                    SlowPtr = SlowPtr->next;//直到两个慢指针相遇
                }
                return WalkerPtr;
            }
        }
        return NULL;
  }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值