题目描述:一个链表中包含环,请找出该链表的环的入口结点。
思路:本题也是一个使用快慢双指针解决的经典问题。受之前的题目【求倒数第k个结点】的启发,我们假设原题中链表尾结点的next不是为空,而是指向我们找到的倒数第k个结点,形成一个有k个结点的环,那么这个结点其实也就是本题中要求的环的入口结点。也就是说只要我们知道本题中环中结点个数,就可以沿用上一题的做法解决问题。
假设有p,q两指针,p每步跨一个结点,q每步跨两个结点。那么经过k步之后q比p多走过的结点数为k。如果链表中没有环,那么q永远在p的前面,两指针不会相遇。如果链表有环,且环中结点个数为r,那么q会进入环内绕圈。如果两指针会在相遇,一定是在环内,并且q比p多绕n圈;也就是说一定存在k,只要满足k=n * r,就能使两指针相遇。
如下图所示,假设链表起点为a,入口结点为b,相遇点为c,ab段长度为m,bc段长度为n,环长度为r。经过上面的证明,我们知道c点一定存在,并且根据两指针走过的路程,存在等式2(m+n+h*r)= m+n+g*r,这样我们得到m=(g-2h)*r-n,也就是说ac段长度是环长度的整数倍。如果p指针重新从链表头出发,q指针从相遇点c出发,步伐一致,那么当p走到b点,q也正好走到b。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
if not pHead or not pHead.next or not pHead.next.next:
return None
slow = pHead.next
fast = slow.next
# 找到相遇点
while fast != slow and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
# 慢指针回到表头,快指针留在相遇点,二者同步往前直到相遇在入口结点
slow = pHead
while slow != fast:
fast = fast.next
slow = slow.next
return slow
return None