【剑指Offer-代码的鲁棒性】编程题23:链表中环的入口节点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路1
首先要判断链表中有没有环:可以利用两个指针,初始都在head,一个指针移动的比较快(一次移动两个节点),另一个指针移动的比较慢(一次移动一个节点),如果两个节点最终相遇了,则说明链表中存在环;如果快指针的下一个节点为nullptr,则说明链表无环。如果链表有环,则快指针和慢指针相遇的节点一定在环中,从相遇节点移动n次可以再次到达相遇节点,则n就是环的长度。此时,再设置两个指针p1和p2,将指针p1先移动n步,然后再同时移动p1,p2,则p1,p2的相遇点就是环的入口。代码如下:
/*
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* meetingNode = getMeetingNode(pHead);
if(meetingNode==nullptr)
return nullptr;
int nodeNumsInLoop = 1; //环中的节点个数
ListNode* temp = meetingNode->next;
while(temp!=meetingNode)
{
temp = temp->next;
nodeNumsInLoop++;
}
ListNode* pSlow = pHead;
ListNode* pFast = pHead;
for(int i=0; i<nodeNumsInLoop; i++)
pFast = pFast->next;
while(pSlow!=pFast)
{
pSlow = pSlow->next;
pFast = pFast->next;
}
return pFast;
}
ListNode* getMeetingNode(ListNode* pHead) //寻找环中的相遇点
{
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
while(pFast->next!=nullptr)
{
if(pFast->next!=nullptr && pFast->next->next!=nullptr)
pFast = pFast->next->next;
if(pSlow->next!=nullptr)
pSlow = pSlow->next;
if(pSlow==pFast)
return pFast;
}
return nullptr;
}
};
思路2
首先判断环是否存在还是和思路 1 类似,使用快慢指针,慢指针每次走一步,快指针每次走两步,如果最终快慢指针相等,则说明有环。否则,如果快指针 fast->next==nullptr,则说明无环。若有环,则将慢指针 slow 回退到链表头,快指针位置不变,也就是相遇的位置,然后快慢指针每次都走一步,如果快慢指针相遇,则相遇点一定是入口。代码如下:
/*
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* slow = pHead;
ListNode* fast = pHead;
while(slow!=nullptr && fast->next!=nullptr){
slow = slow->next;
fast = fast->next->next;
if(slow==fast) break;
}
if(fast->next==nullptr) return nullptr; // 无环
slow = pHead;
while(slow!=fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
};