第23题:链表中环的入口结点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
考点
1.鲁棒性:存在环;不为空链表;指针下一个节点存在时才往下走。
2.复杂问题分解成简单问题 。
思路
1.判断链表中存在环:
两个指针,同时从头节点出发,fast的走两步,slow的走一步。
如果fast追上了slow,则说明存在环。
如果fast走到了末尾,fast->next==nullptr,fast都没有追上slow,说明不存在环。
2.判断环的入口:
如果环中有n个节点,fast和slow同时从head出发,先让fast走n步,然后slow和fast一个速度走。
如果slow和fast相遇了,此时相遇的节点就是环的入口。
3.计算环的个数:
第一步判断是否有环时,相遇的节点一定在这个环中,从这个节点开始走,并且计数,如果回到了该节点,就是环的节点个数。
第一遍
//鲁棒性:是否存在环
ListNode* MeetingNode(ListNode* pHead)
{
//1.鲁棒性1:头节点为空,返回空
if (!pHead)
return nullptr;
//2.slow先走一步,注意鲁棒性2: 如果slow不存在返回nullptr
ListNode* slow = pHead->m_pNext;
if (!slow)
return nullptr;
//3.fast走两步
ListNode* fast = slow->m_pNext;
//4.如果fast和slow都存在的情况
while (fast&& slow)
{
//4.1 如果两个指针相遇了,返回相遇节点
if (fast == slow)
return fast;
//4.2 slow先走到下一步
slow = slow->m_pNext;
//4.3 fast要走两步,先走第一步,在测试鲁棒性之后,走第二部
fast = fast->m_pNext;
if (fast)
{
fast = fast->m_pNext;
}
}
//5.如果fast和slow都没有相遇,返回nullptr
return nullptr;
}
//找到入口
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
//1.鲁棒性检查:判断是否含有环,如果没有,返回空
ListNode* MeetNode = MeetingNode(pHead);
if (!MeetNode)
return nullptr;
//2.设置环内节点计数参数,从1开始
int count = 1;
//3.新建一个指针从相遇节点的下一个走
ListNode* Node = MeetNode->m_pNext;
//4.边走边计数,走到遇到自己为止,
while (Node&& (Node != MeetNode))
{
Node = Node->m_pNext;
count++;
}
Node = nullptr;
//5.先让fast走n步
ListNode* fast= pHead;
for(int i=0;i<count;i++)
{
fast= fast->m_pNext;
}
//6.slow开始走
ListNode* slow = pHead;
while (slow&&fast)
{
if (slow == fast)
return fast;
slow = slow->m_pNext;
fast = fast->m_pNext;
}
}
第二遍
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
class Solution {
public:
//检查是否有环,并返回环中的节点
ListNode* MeetingNode(ListNode* pHead)
{
//1.鲁棒性测试1:空链表
if(!pHead)
return nullptr;
//2.鲁棒性2:头节点的next为空
if(!pHead->next)
return nullptr;
//3.定义slow和fast,同时出发,slow走一步,fast走两步
ListNode* slow=pHead->next;
ListNode* fast=slow->next;
//4.鲁棒性3:fast走到第二部如果不存在
if(!fast)
return nullptr;
//5.while循环条件:fast和slow都存在的情况
while(fast&&slow)
{
//5.1 如果相遇,返回相遇点
if(fast==slow)
return fast;
//5.2 slow走一步
slow =slow->next;
//5.3 fast走两步,先判断走的第一步是否为空,如果为空,就不是循环列表,也不能走第二步,返回
fast=fast->next;
if(!fast)
return nullptr;
else
fast=fast->next;
}
//6. 如果没有相遇,返回空
return nullptr;
}
//找到环中的入口
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
//1.如果不存在环,返回空
ListNode* meetNode= MeetingNode(pHead);
if(!meetNode)
return nullptr;
//2.计算环的节点个数
int count=1;
ListNode* node=meetNode->next;
while(node!=meetNode)
{
node=node->next;
count++;
}
node=nullptr;
//3.寻找入口节点
ListNode* fast = pHead;
ListNode* slow = pHead;
//3.1 fast先走N步
for(int i=0;i<count;i++)
{
fast=fast->next;
}
//3.2 slow开始走,如果fast和slow相遇了就是环的入口
while(slow!=fast)
{
slow= slow->next;
fast=fast->next;
}
return fast;
}
};
注意:如果有循环链表是不会到nullptr的,所以不用判断nullptr了。