topic

Given a head node head of a linked list, return the first node that the linked list begins to enter the ring. Returns null if the linked list has no cycle.

If there is a node in the linked list that can be reached again by continuously tracing the next pointer, there is a cycle in the linked list. To represent a ring in a given linked list, the evaluation system internally uses the integer pos to represent the position in the linked list where the tail of the linked list is connected (index starts at 0). If pos is -1, there are no cycles in the list. Note: pos is not passed as a parameter, just to identify the actual situation of the linked list.

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

Traverse + hash

The easiest way is to traverse each node and store the node in a dictionary. When traversing the next node, see if the node already exists in the dictionary. If so, it means that the node is the ring entry node.

def detectCycle(head: ListNode) -> ListNode:
    hashTable = {}
    while head:
        if head in hashTable:
            return head
        hashTable[head] = 1
        head = head.next
    return None

Time complexity: O(n)
Space Complexity: O(n)

double pointer

To understand the idea of two pointers, you need to draw a picture and calculate the equivalent relationship of the moving distance to understand:
image.png
At the head node of the linked list, define two pointers: fast and slow
fast moves 2 nodes at a time, slow moves 1 node at a time

If the linked list has no cycle, the fast pointer will move to null first
If the linked list has a ring, fast enters the ring first, slow enters the ring later, and eventually they will definitely meet at a node in the ring

Suppose the node when fast and slow meet for the first time is as shown in the figure
a represents the number of nodes from the head node of the linked list to the entry node of the ring
b represents the number of nodes from the entry node of the ring to the node where fast and slow meet for the first time
c represents the number of nodes from the first encounter node to the entry node of the ring

At this time, the number of nodes that fast travels through is a+b+n(c+b), where n represents the number of turns that fast circles in the ring
The number of nodes slow goes through is a+b
fast and slow move together, so their number of moving steps is the same at any time, but fast moves 2 nodes at a time. Under the same number of steps, the number of nodes moved by fast should be twice that of slow.
So a+b+n(c+b) = 2(a+b)
Arrange the equation to get: a=n(b+c)-b = nb+nc-b = (n-1)b+nc = (n-1)b+(n-1)c+c=(n-1) )(b+c)+c
get a = (n-1)(b+c)+c
That is to say: the distance from the head node to the ring entry node = the distance from the first encounter node to the ring entry node + (n-1) the length of the ring
If there are two pointers a and b at this time, a starts to move from the head node, and b starts to move from the node where slow and fast meet for the first time, moving one node at a time, a and b will eventually meet at the ring entry node.
At this point, slow is already in the position of the first encounter node, so when slow and fast meet for the first time, define a pointer at the head node, move one node at a time with slow, and the node they meet is Ring entry node.

def detectCycle(self, head: ListNode) -> ListNode:
    # 排除空链表情况
    if not head:
        return None
    fast,slow,ptr = head,head,head

    while fast:
        # 排除链表只有一个节点的情况
        if not fast.next:
            return None
        # 移动fast和slow
        slow = slow.next
        fast = fast.next.next
        # fast和slow相遇时
        if slow == fast:
            # ptr和slow开始移动,直到相遇
            while ptr != slow:
                slow = slow.next
                ptr = ptr.next
            # 相遇时,返回该节点,就是入口节点
            return ptr       
    return None

# 跟简洁优雅版
def detectCycle(self, head: ListNode) -> ListNode:
    fast, slow = head, head
    while True:
        if not (fast and fast.next): return
        fast, slow = fast.next.next, slow.next
        if fast == slow: break
    fast = head
    while fast != slow:
        fast, slow = fast.next, slow.next
    return fast

Time complexity: O(N), where N is the number of nodes in the linked list. When initially judging whether the fast and slow pointers meet, the distance traveled by the slow pointer will not exceed the total length of the linked list; then when finding the loop entry point, the distance traveled will not exceed the total length of the linked list. So the total execution time is O(N)+O(N)=O(N).

Space complexity: O(1). We only use three pointers slow, fast, and ptr.


Ethan
140 声望11 粉丝

水平较低,只是记录,谨慎参阅


引用和评论

0 条评论