剑指Offer——链表中环的节点入口
题目:如果链表中存在环,则找出环的入口节点,如下图,环的入口为节点3
首先要想办法确认链表中是否有环。链表中有环表明链表中没有尾节点,因此遍历的时候会一直循环下去永不停止。用一个指针的话很难确认表中是否有环。
因此我们尝试用两个指针,让一个指针走快一点,一个指针走慢一点,当两个指针指向的节点一样时,可以确认链表中一定有环。否则如果当走得快的那个指针已经到尾节点,链表肯定就没有环了。
之后尝试确认环的入口,由图可以看出对于环入口,其是环的第一个元素,环的前一个元素在环之外。我们依然可以用两个指针,如果第一个指针先往前走环的长度N步,然后两个节点再以一样速度移动,那么当两个节点第一次重合时,即是环的入口。
如上图,环的长度为4,第一个指针从节点1开始前进4步到节点5,然后第二个指针从节点1开始,两个指针各走前进两个节点后在节点3中重合,因此节点3为环的入口。
因此问题的关键在于想办法算出环的节点数,这个并不太难。在环中一直向前走,走了A步后返回原节点,即可确认节点长度为A。
以下为实现代码
ListNode* EntryNodeInListLoop(LinkList a){
if(a==nullptr) return nullptr;
ListNode *N1 = a;//节点每次走1步
ListNode *N2 = a;//节点每次走2步
//当两个节点重合,表明链中有环
//当N2走到链表末尾,表明链表无环
N2 = N2->Next;
while(N2!=nullptr&&N1!=N2){
N1 = N1->Next;
N2 = N2->Next;
if(N2) N2 = N2->Next;
}
if(N2==nullptr) return nullptr;
//函数到此处仍未返回,证明表中有环
//确认环的长度
int len = 1;
N2 = N2->Next;
while(N1!=N2){
++len;
N2 = N2->Next;
}
//将两个指针重设为头指针,N2先走len步,然后再一起走
//当两个指针重合,则该处为环入口
N1 = a; N2 = a;
for(int i = 0; i < len; ++i) N2 = N2->Next;
while(N1!=N2){
N1 = N1->Next;
N2 = N2->Next;
}
return N1;
}
算法的完整代码在这里
总结
本题难度在于不能一步获得结果,需要把问题分成
- 确认链表中有环
- 计算环的长度
- 获得环的入口
而每一个子问题的解法都不太难。