【LeetCode題解】142_環形鏈表2(Linked-List-Cycle-II)

目錄

描述

給定一個鏈表,返回鏈表開始入環的第一個節點。 如果鏈表無環,則返回 null

說明:不允許修改給定的鏈表。

進階
你是否可以不用額外空間解決此題?

解法一:哈希表

思路

最直接的解法就是利用一個集合保存每次遍歷的節點的引用。之後,從鏈表頭開始遍歷,每遍歷一個節點,就判斷該節點的引用是否在集合中,如果不在集合中,則將該節點的引用放入集合中;如果在集合中,則返回該節點的引用(環的入口)。當然,如果能遍歷到鏈表尾部,此時鏈表無環,返回 null

Java 實現

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */

import java.util.Set;
import java.util.HashSet;

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode curr = head;
        Set<ListNode> nodesSeen = new HashSet<>();
        while (curr != null) {
            if (nodesSeen.contains(curr)) {
                return curr;
            }
            nodesSeen.add(curr);
            curr = curr.next;
        }
        return curr;
    }
}

Python 實現

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        curr = head
        nodes_seen = set()
        while curr:
            if curr in nodes_seen:
                return curr
            nodes_seen.add(curr)
            curr = curr.next
        return curr

複雜度分析

  • 時間複雜度:\(O(n)\)
  • 空間複雜度:\(O(n)\)

解法二:雙指針

思路

LeetCode 第 141 題一樣,如果不想佔用額外的空間的話,可以採用雙指針的方式。

假設鏈表的起始節點爲 A,環的入口節點爲 B,兩個指針(快慢指針)相交節點爲 C,AB 兩點之間的長度爲 \(x\),BC 兩點之間的長度爲 \(y\),CB 兩點之間的長度爲 \(z\)。慢指針 slow 走過的長度爲 \(x+y\),快指針 fast 爲了“趕上”慢指針,應該走過的長度爲 \(x + y + z + y\),同時,由於快指針的速度是慢指針的兩倍,因此相同時間內,快指針走過的路程應該是慢指針(走過的路程)的兩倍,即

\[ x + y + z + y = 2 (x + y) \]
化簡得,
\[ x = z \]
因此,如果此時有另外一個慢指針 slow2 從起始節點 A 出發,則兩個慢指針會在節點 B (環的入口)相遇。

Java 實現

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            
            if (slow == fast) {
                ListNode slow2 = head;
                while (slow != slow2) {
                    slow = slow.next;
                    slow2 = slow2.next;
                }
                return slow;
            }
        }
        return null;
    }
}
// Runtime: 1 ms
// Your runtime beats 100.00 % of python submissions.

Python 實現

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def detectCycle(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        slow, fast = head, head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            
            if slow == fast:
                slow2 = head
                while slow != slow2:
                    slow = slow.next
                    slow2 = slow2.next
                return slow
        return None
# Runtime: 44 ms
# Your runtime beats 99.73 % of python submissions.

複雜度分析

  • 時間複雜度:\(O(n)\),其中 \(n\) 表示鏈表的長度。最壞的情況下(鏈表有環),需要迭代的次數爲 \(x + y + z = n\) 次,因此時間複雜度爲 \(O(n)\)
  • 空間複雜度:\(O(1)\),只需要存儲 3 個引用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章