#142. 环形链表
题目
给定一个链表,返回链表入环的第一个节点。如果无环,返回Null。
解答
HashSet解法
设置cur指针,一直把cur指针更新为cur.next。
如果哪一次新看的cur在HashSet visited里面,那么证明该节点是链表入环的第一个节点。
每一次check完都要记得把该节点加入visited里。
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
visited = set()
cur = head
while(cur):
if cur in visited:
return cur
visited.add(cur)
cur = cur.next
return None
快慢指针解法
- 快指针走2步,慢指针走1步,找相遇点。如果已经走到尽头仍然没有相遇点,则代表无环。
- 快指针从头出发,慢指针从相遇点出发,相遇位置为链表内循环的起点。
- 以下公式的最后一行解释了为什么两个指针分别从头,从相遇点开始走,可以最终碰面于循环节点 😉
m + k n + y = 2 ( m + y ) k n = m + y k n = m + n − x m = x + n ( k − 1 ) m + kn + y = 2(m + y)\\ kn = m + y\\ kn = m + n - x\\ m = x + n(k-1) m+kn+y=2(m+y)kn=m+ykn=m+n−xm=x+n(k−1)
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
fast = head
slow = head
#fast 和 fast.next必须同时为非None,这样才可以更新fast为非None节点
while fast and fast.next:
fast = fast.next.next
slow = slow.next
if fast == slow: #如果任何一个节点他们相交,直接break,记录该节点为slow的起始位置
break
#fast最终如果是None或者fast.next为None,那代表了该链表已经走到尽头,无环
if fast == None or fast.next == None:
return None
point = fast
fast = head
while fast != slow:
fast = fast.next
slow = slow.next
return fast
收获
- 快慢指针的解法太聪明了!!!LOLLOL❀
- 慢指针刚从循环起点出发,快指针可以在任意位置。快指针离慢指针的距离 < m。
- 假设慢指针走了超过一圈的距离,则快指针走了超过两圈的距离,快指针至少比慢指多走一圈 > m。
- 所以在慢指针走路的这段时间内,快指针可以追上慢指针 😉