链表环的入口

156 阅读1分钟

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

不允许修改给定的链表。

是否可以使用 O(1) 空间解决此题?

分析:

首先使用快慢指针方法判断该链表是否有环,如果有环:

假设从起始节点到环入口长度为a,环的长度为r

慢指针已经走过环内的长度为x,环内剩余长度为y

则快慢指针相遇时,慢指针一共走了 a + x

快指针一共走了 a + nr + x

因为快指针速度是慢指针的2倍

则 2(a + x) = a + nr + x,其中r = x + y

 a + x = nr
 ==> a + x = (n - 1)r + x + y
 ==> a = (n - 1)r + y

由此可以得出,如果另外设置两个指针,一个A指针从起始节点走,另一个指针B从快慢指针相遇节点走,则一定和A相遇,相遇节点即为环的入口

代码实现(C):

只需要在判断链表是否有环的代码中,让fast指针指向head,和慢指针一起循环

struct ListNode *detectCycle(struct ListNode *head) {
    if (head == NULL) {
        return NULL;
    }
    struct ListNode *fast = head,*slow = head;
    while (fast && fast->next) {
        slow = slow->next; // 慢指针每次走一步
        fast = fast->next->next; // 快指针走两步
        if (slow == fast) {
            fast = head;
            while (fast != slow) { // 两个指针以同样的速度走
                fast = fast->next;
                slow = slow->next;
            }
            return slow;
        }
    }
    return NULL
}

代码实现(swift):

extension ListNode : Equatable {
    public static func == (lhs: ListNode, rhs: ListNode) -> Bool {
        return lhs === rhs
    }
}

class Solution {
    func detectCycle(_ head: ListNode?) -> ListNode? {
        var slow = head
        var fast = head
        while fast != nil && fast?.next != nil {
            slow = slow!.next
            fast = fast!.next!.next
            if fast == slow {
                fast = head
                while fast != slow {
                    fast = fast!.next
                    slow = slow!.next
                }
                return fast
            }
        }
        return nil
    }
}