题目
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路
- 首先用两个快慢指针,快指针一次走一步,慢指针一次走两步。如果快指针走到了nullptr,说明没有环。如果他俩相遇,则说明有环。记录下相遇的位置。
- 用两个指针,一个指向链表首部,一个指向前面快慢指针相遇的位置。两个指针同时往前走,最后相遇的地方即为环的入口结点。
证明
- 如果有环,快慢指针一定会相遇。
- 如果有环,在某一时刻,快慢指针都会在在环里,此时快指针一次走两步,慢指针一次走一步,所以每次快慢指针的距离缩短,最终会相遇。
- 指向链表首部的指针和指向快慢指针相遇位置的指针,同时向前走,相遇的地方为链表入口结点。
- 如上图所示为一个链表。到相遇点的时候有:
- 慢指针走过的距离有 a + b a+b a+b。
- 快指针走过的距离为 a + b + k ( b + c ) a+b+k(b+c) a+b+k(b+c),k表示快指针走过的圈数, k > = 1 k>=1 k>=1。
- 快指针的速度是慢指针的两倍,所以有 a + b + k ( b + c ) = 2 ( a + b ) a+b+k(b+c)=2(a+b) a+b+k(b+c)=2(a+b),化简得 a = ( k − 1 ) ( b + c ) + c a=(k-1)(b+c)+c a=(k−1)(b+c)+c。
- 对于 a = ( k − 1 ) ( b + c ) + c a=(k-1)(b+c)+c a=(k−1)(b+c)+c的理解。指针从链表头开始走 a a a步会到达入口结点,指针从快慢指针相遇点走若干圈后再走c的距离也能到达入口结点。
代码
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if ( pHead == nullptr )
return nullptr;
// 快慢指针求交点
ListNode* jointNode = findJoinNode( pHead );
// 求入口结点
ListNode* entryNode = findEntryNode( pHead, jointNode );
return entryNode;
}
ListNode* findJoinNode( ListNode* pHead ) {
ListNode* slow = pHead, fast = pHead;
ListNode* node = nullptr;
while ( fast && fast->next ) {
slow = slow->next;
fast = fast->next->next;
if ( slow == fast ) {
node = slow;
break;
}
}
return node;
}
ListNode* findEntryNode( ListNode* pHead, ListNode* jointNode ) {
if ( jointNode == nullptr )
return nullptr;
while ( pHead != jointNode ) {
pHead = pHead->next;
jointNode = jointNode->next;
}
return pHead;
}
};
``