版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/82455280
面试题23:链表中环的入口结点
一个单向链表中包含环,如何找出环的入口结点?
包含环的链表,在进入环以后就会一直在环里循环。注意,因为单向链表是只有一个next的,所以链表的环一定是下图左边的形状,而不会像右边这样有走出去的可能:
[1]判断有没有环
因为进了环就出不来,使用快慢指针,如果走得快的指针追上了走得慢的指针,那么链表就包含环。
[2]寻找入口结点
只要让两个指针相差一个环的长度。即若环的长度为n,则先让P1在链表上走n步(差出1个环的距离),然后P2从头结点开始,两个指针以相同的速度向前移动,当P2指向入口结点时,P1因为比P2领先一个环的长度,也绕回来指向了入口结点,即此时P1P2是重合的。
[3]解决环的长度问题
快慢指针时,两个指针相遇的结点一定在环中,所以只要标记此处,然后向前边走边计数,直到再回到此处即可。
总之本题就是先用快慢指针进入环内某个结点,然后跑一圈找到环长,再用双指针从头结点开始跑一下找到环起始结点。
#include<bits/stdc++.h>
#include "../Utilities/List.h"
using namespace std;
//输入链表的头结点,用快慢指针找到环内的一个结点,如果不存在环返回nullptr
ListNode* MeetingNode(ListNode* pHead) {
//输入非空校验
if(pHead == nullptr)
return nullptr;
//慢指针:初始在头结点后1个指针
ListNode* pSlow = pHead->m_pNext;
if(pSlow == nullptr)//每次都要判断非空,确保有环
return nullptr;
//快指针:初始在头结点后2个指针
ListNode* pFast = pSlow->m_pNext;
//每次判断当下非空,再往下走
//实际上根本没必要再去检查慢指针了,这里还是保留作者的写法
//[注意]有环的链表往后走绝不会走到空
while(pFast != nullptr && pSlow != nullptr) {
if(pFast == pSlow)//如果两指针相遇
return pFast;//那么该处一定是环内一结点,将其返回
//慢指针每次走一步
pSlow = pSlow->m_pNext;
//快指针每次走两步
pFast = pFast->m_pNext;//第一步可以直接走,因为while循环里判断过了
if(pFast != nullptr)//第二步就需要再判断一次再走了
pFast = pFast->m_pNext;
}
//如果while结束了还没返回,说明发现了空,链表没有环
return nullptr;
}
//输入链表头结点,找到链表中环的起始结点
ListNode* EntryNodeOfLoop(ListNode* pHead) {
//找到环内的某个结点
ListNode* meetingNode = MeetingNode(pHead);
if(meetingNode == nullptr)//如果返回了nullptr
return nullptr;//说明不存在环,这个函数也返回nullptr
//得到环中结点的数目
int nodesInLoop = 1;//从1(刚刚找到的这个结点)开始计数
ListNode* pNode1 = meetingNode;//找到的结点用于标记当下位置,另找一个跑动的指针
//只要接下来不是标记位置,就说明没遍历完这个环
while(pNode1->m_pNext != meetingNode) {
pNode1 = pNode1->m_pNext;//跑动指针向下走
++nodesInLoop;//计数+1
}
//至此,已经得到了环长(环中结点数),保存在nodesInLoop中
//双指针法,先移动pNode1为先行
pNode1 = pHead;//从头结点开始移动
for(int i = 0; i < nodesInLoop; ++i)//次数为环中结点的数目
pNode1 = pNode1->m_pNext;
ListNode* pNode2 = pHead;//pNode2踩上头结点
//两个指针同时向后移动,直到相遇为止
while(pNode1 != pNode2) {
pNode1 = pNode1->m_pNext;
pNode2 = pNode2->m_pNext;
}
//双指针相遇的位置即为所求的环的起始结点
return pNode1;
}
int main() {
//创建结点
ListNode* pNode1 = CreateListNode(1);
ListNode* pNode2 = CreateListNode(2);
ListNode* pNode3 = CreateListNode(3);
ListNode* pNode4 = CreateListNode(4);
ListNode* pNode5 = CreateListNode(5);
//连成带环的链,即5绕回到3上
ConnectListNodes(pNode1, pNode2);
ConnectListNodes(pNode2, pNode3);
ConnectListNodes(pNode3, pNode4);
ConnectListNodes(pNode4, pNode5);
ConnectListNodes(pNode5, pNode3);
//计算起始结点,并输出
PrintListNode(EntryNodeOfLoop(pNode1));
//销毁链表
DestroyList(pNode1);
return 0;
}