leetcode q141、q142有感(Linked List Cycle I 、 II )

827 阅读3分钟

LeetCode Q141 Linked List Cycle

Given a linked list, determine if it has a cycle in it. To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to.

If pos is -1, then there is no cycle in the linked list.

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */

1思路 & 解法

1.1 思路一:每走过一步留下脚印👣,之后遇见脚印,输出有cycle;

代码如下:

bool hasCycle(struct ListNode *head) {
    int impossibleNum = INT_MIN + 55467;
    while (head != NULL) {
        if (head->val == impossibleNum) {
            return true;
        }
        head->val = impossibleNum;
        head = head->next;
    }
    return false;
}

1.1.1 思路一扩展:显然上述代码有个弊端,修改了原链表;如何不修改原列表去记录脚印👣?

思路参考,用字典,用ListNode的address作为key,每走一个ListNode判断该node的address是否在字典内,如果链表走到底则没有cycle,否则则有cycle(且第一个遇到的为cycle的head)

由于c的标准库没有dictionary(reference),此处代码略

1.2 思路二:快慢指针,重叠以后证明有cycle

思路二解释:从head node开始,一个指针一次走一个node,另一个一次走两个node,,如果两者能够相遇,则证明有cycle

常识来想很简单,两个人在操场跑步,一个人跑的速度是另一个人的2倍,他们初始位置不限制;但慢的人跑一圈,快的会跑两圈,他们肯定会在某处相遇

证明:

问题可以等价为:对于cycle里任意两个node(节点),一个以1倍速同方向走,一个以2倍速同方向走,最终一定相遇;
设cycle长为l(l>0),k为两node初始距离(-l < k < l)
问题可视为 2x % l = (x + k) % l ,在 x > 0 情况下一定有解
即 2x = x + k + m * l (m可谓任意正整数) 在 x > 0 情况下有解
显然,
当k < 0时,令m = 1, 即x = l + k;为其中一个解, 
当k >= 0 时,x = k, 为其中一个解;

代码:

bool hasCycle(struct ListNode *head) {
    if (head == NULL) {
        return false;
    }
    struct ListNode *pSlow = head;
    struct ListNode *pFast = head;
    while (pSlow->next != NULL && pFast->next != NULL && pFast->next->next != NULL) {
        pSlow = pSlow->next;
        pFast = pFast->next->next;
        if (pSlow == pFast) {
            return true;
        }
    }
    return false;
}

LeetCode Q142 Linked List Cycle II

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list.

Note: Do not modify the linked list.

Follow-up: Can you solve it without using extra space?

思路 & 解法

考虑到Do not modify the linked listCan you solve it without using extra space?,q141思路一不适用,考虑q141思路二上做文章;

但是,如何拿到cycle的headnode?

设cycle前node集为pre,pre长度为p,cycle长为l,相遇节点为cycle的第k个node,相遇时慢指针pSlow走的步数为total。
则有: 
①2total = p + k + m * l (m ∈ Z) 
②total = p + k + n * l (n < m, n 属于 Z)
① - ②得 ③total = (m - n) * l 
可得p + k = (m - 2n) * l

p + k 是 l的倍数,如何使用该条件得到loopHead?

我们知道,当我们快慢指针相遇时,我们当前的节点位置为k,那么让慢指针k再走p个节点,一定为loopHead节点。
但如何走一个p节点?
brilliant idea:再让一个慢指针从head节点走,两指针第一次相遇,则两者走了p节点!且该节点就是要求的cycle的headnode!!!

参考图:

代码:

struct ListNode *detectCycle(struct ListNode *head) {
    if (head == NULL) {
        return head;
    }
    struct ListNode *pSlow = head;
    struct ListNode *pFast = head;
    bool isCycle = false;
    while (pSlow->next != NULL && pFast->next != NULL && pFast->next->next != NULL) {
        pSlow = pSlow->next;
        pFast = pFast->next->next;
        if (pSlow == pFast) {
            isCycle = true;
            break;
        }
    }
    if (isCycle) {
        pFast = head;
        while (pFast != pSlow) {
            pFast = pFast->next;
            pSlow = pSlow->next;
        }
        return pFast;
    }
    return NULL;
}

project