快慢指针法巧解链表中环的入口节点
问题描述分析 与 快慢指针巧解思路
1. 求链表中环的入口节点
如果一个链表中包含环,如何找出环的入口呢?
例如,在图示的链表中,环的入口节点是节点 3。
2. 快慢指针解题思路
解决这个问题的第一步应该是如何确定一个链表是否有环:
确认链表中是否有环,我们仅仅需要两个指针,一个快指针一次遍历两格,一个满指针一次遍历一格,如果快指针能遍历到尾巴,则无环,如果快指针能追上慢指针,则有环。
第二步是去找到环的入口:
我们还是定义两个指针来解决这个问题,先定义两个指针都指向链表的头节点,如果链表的环有
n 个节点,那么快指针指向到 链表的第 n+1 个节点(保持 快慢指针之间的距离为n-1(不计算快慢指针的指向的两个节点)),慢指针指向头节点,之后判断是否相等,所指节点相等则 该节点是入口,反之快慢指针继续逐步遍历。
这时我们会发现:我们怎么知道 链表的环 有几个节点呢?
哈哈,我们可以使用暂停法,就是说我们第一步确认链表有环后,快指针暂停,慢指针逐步递增,这时计数器 +1 ,直到慢指针追上快指针,就能知道环的节点个数。
快慢指针解题代码
链表节点定义:
struct ListNode{
int m_nValue;
ListNode* m_pNext;
}
声明解题函数:
ListNode* MeetingNode(ListNode* pHead); //第一步判断是否有环以及得到相遇节点
ListNode* EntryNodeOfLoop(ListNode* pHead);
保证代码的鲁棒性,注重防御性编程,加油!
功能测试: (链表中包含或者不包含环;链表中有多个或者只有一个节点)
特殊输入测试:(链表头节点为 nullptr 节点)
ListNode* MeetingNode(ListNode* pHead){ //判断是否有环以及得到相遇节点
if(!pHead)
return nullptr;
ListNode* pSlow = pHead;
ListNode* pQuick = pHead;
while(pQuick->next!=nullptr){
pQuick = pQuick->m_pNext;
if(pQuick->next != nullptr)
pQuick = pQuick->m_pNext;
else
break;
pSlow = pSlow ->next;
if(pSlow == pQuick)
return pQuick;
}
return nullptr;
}
ListNode* EntryNodeOfLoop(ListNode* pHead){{
auto meetingNode = MeetingNode(pHead);
if(meetingNode == nullptr)
return nullptr;
//第二步 统计环节点个数
ListNode* pNode1 = meetingNode;
int numsOfLoop = 0;
while(pNode1 != meetingNode){
pNode1 = pNode->m_pNext;
++ numsOfLoop;
}
//快慢指针找寻 链表环开始节点。
pNode1 = pHead;
for(int i=0;i!= numsOfLoop; ++i){
pNode1 = pNode1->m_next;
}
ListNode* pNode2 = pHead;
while(pNode2 != pNode1){
pNode1 =pNode1->m_pNext;
pNode2 =pNode2->m_pNext;
}
return pNode2;
}