LeetCode 142. Linked List Cycle II(环形链表 II)

184 阅读2分钟

leetcode.com/problems/li…

Discuss:www.cnblogs.com/grandyang/p…

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

There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the node that tail's next pointer is connected to. Note that pos is not passed as a parameter.

Notice that you should not modify the linked list.

Example 1:

image.png

Input: head = [3,2,0,-4], pos = 1
Output: tail connects to node index 1
Explanation: There is a cycle in the linked list, where tail connects to the second node.

Example 2:

image.png

Input: head = [1,2], pos = 0
Output: tail connects to node index 0
Explanation: There is a cycle in the linked list, where tail connects to the first node.

Example 3:

image.png

Input: head = [1], pos = -1
Output: no cycle
Explanation: There is no cycle in the linked list.

Constraints:

The number of the nodes in the list is in the range [0, 104].

-105 <= Node.val <= 105

pos is -1 or a valid index in the linked-list.

Follow up: Can you solve it using O(1) (i.e. constant) memory?

解法一:

使用 HashSet 记录。

/**
 * Example:
 * var li = ListNode(5)
 * var v = li.`val`
 * Definition for singly-linked list.
 * class ListNode(var `val`: Int) {
 *     var next: ListNode? = null
 * }
 */

class Solution {
    fun detectCycle(head: ListNode?): ListNode? {
        if (head?.next == null) {
            return null
        }

        val cacheSet = hashSetOf<ListNode>()
        var tempHead = head
        
        while (tempHead != null) {
            cacheSet.add(tempHead)
            if (cacheSet.contains(tempHead.next)) {
                return tempHead.next
            }
            tempHead = tempHead.next
        }
        return null
    }
}

解法二:

快慢指针解法。这个求单链表中的环的起始点是之前那个判断单链表中是否有环的延伸,可参考之前那道 Linked List Cycle。这里还是要设快慢指针,不过这次要记录两个指针相遇的位置,当两个指针相遇了后,让其中一个指针从链表头开始,此时再相遇的位置就是链表中环的起始位置,为啥是这样呢,这里直接贴上热心网友「飞鸟想飞」的解释哈,因为快指针每次走2,慢指针每次走1,快指针走的距离是慢指针的两倍。而快指针又比慢指针多走了一圈。所以 head 到环的起点 + 环的起点到他们相遇的点的距离与环一圈的距离相等。所以,从 head 走到环起点和相遇点走到环起点的距离也是相等的。

/**
 * Example:
 * var li = ListNode(5)
 * var v = li.`val`
 * Definition for singly-linked list.
 * class ListNode(var `val`: Int) {
 *     var next: ListNode? = null
 * }
 */

class Solution {
    fun detectCycle(head: ListNode?): ListNode? {
        if (head?.next == null) {
            return null
        }
        
        var slowNode: ListNode? = head
        var quickNode: ListNode? = head

        while (quickNode?.next != null) {
            slowNode = slowNode?.next
            quickNode = quickNode.next?.next
            if (slowNode == quickNode) {
                break
            }
        }

        if (quickNode?.next == null) {
            return null
        }

        slowNode = head
        
        while (slowNode != quickNode) {
            slowNode = slowNode?.next
            quickNode = quickNode?.next
        }
        return quickNode
    }
}