1. 程式人生 > >【LeetCode題解】142_環形連結串列2(Linked-List-Cycle-II)

【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 個引用