分为三步:
一、判断是否有环:
两个指针,fast和slow。初始两个指针都指向链表的头结点,fast指针每次前进两步,slow每次前进一步。若链表中有环,两个指针必定会相遇。证明如下:
当链表没有环的时候,fast指针某一时刻为空指针。当链表有环,设k为环的入口结点,环的长度为m,则当slow遍历到结点k时,fast所在的位置为(2k-k)%m=k%m。如果能构计算出两个结点相遇的结点位置,则同时证明了两个结点会相遇。设slow经过x个结点与fast相遇,则有(k%m + 2x)%m = x%m。
k%m+2x-x=ma(其中a>=1,因为当a=0时,表示两个结点在第一环未遍历完的时候就已经相遇,这是不可能的,slow永远在fast的后面)
x=ma-k%m=ma-(k-mb)=m(a+b)-k>=0,其中,b>=0,m>=k(m>=k的含义是两个结点相遇的位置总是在环中)。得证。
二、求环的长度:
在判断是否有环的过程中记录两个结点相遇的位置p,从结点p开始遍历链表,当当前结点再次指向结点p时,当前节点走完了一环。此时,也就知道了环的长度len。
三、求环的入口结点:
两个指针,fast和slow。初始均指向链表的头结点,fast先走len个位置,此时slow也开始走。当fast->next == slow时,表明找到了环的入口结点slow。
代码如下:
#include <iostream>
using namespace std;
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (!pHead) return nullptr;
int len = 0, count = 0;
ListNode* result;
ListNode* ppre = pHead;
ListNode* pcur = pHead;
ListNode* temp;
if (isloop(temp, pHead)) {
len = Looplength(temp);
while (++count < len) {
ppre = ppre->next;
}
while (ppre->next != pcur) {
ppre = ppre->next;
pcur = pcur->next;
}
result = pcur;
return result;
}
return nullptr;
}
bool isloop(ListNode* &temp, ListNode* pHead) {
ListNode* ppre;
ListNode* pcur;
if (pHead->next) {
ppre = pHead->next->next;
pcur = pHead->next;
}
else
{
return false;
}
bool result = false;
while (ppre && pcur) {
if (ppre == pcur) {
temp = pcur;
result = true;
break;
}
if (ppre->next) {
ppre = ppre->next->next;
}
pcur = pcur->next;
}
return result;
}
int Looplength(ListNode* pcur) {
int length = 0;
const ListNode* p = pcur;
while (pcur->next != p) {
length++;
pcur = pcur->next;
}
length++;
return length;
}
};
int main() {
ListNode* pHead = new ListNode(1);
ListNode* p1 = new ListNode(2);
ListNode* p2 = new ListNode(3);
ListNode* p3 = new ListNode(4);
pHead->next = p1;
p1->next = p2;
p2->next = p3;
p3->next = p1;
Solution s;
ListNode* result = s.EntryNodeOfLoop(pHead);
cout << result->val;
system("pause");
return 0;
}