题目描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表。
解题思路
1.哈希解法
简而言之就是遍历结点,找重复地址。如果遍历完毕无重复就返回空。
简单粗暴。
代码
class Solution
{
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
unordered_set<ListNode*> st;
while (pHead) {
if (st.find(pHead) == st.end())
{
st.insert(pHead);
pHead = pHead->next;
}
else
{
return pHead;
}
}
return nullptr;
}
};
2.双指针
解题步骤
1)初始化两个指针,指向pHead。定义快指针fastPtr每次走两步,慢指针slowPtr每次走一步。
2)两个指针在第一次相遇时停止。
3)fastPtr指向pHead,slowPtr保持原地不动。
4)变化fastPtr每次走一步,下次相遇即是环的入口结点。
证明
设AB距离x,BC距离y,CB距离z。fastPtr在第一次相遇前已经走了n圈,slowPtr在第一次相遇前已经走了m圈。
则fastPtr第一次相遇总路程为:
slowPtr总路程为:
根据速度关系可得:
移项化简可得:
进一步设r为圆的周长,即 ,则
上式中 ,也就是整数个圆周 + CB的路程。
因此,当fastPtr和slowPtr分别从pHead和C点以相同速度出发,一定会同时恰好到达B点,即环的入口。
代码
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode* fastPtr = pHead;
ListNode* slowPtr = pHead;
while (fastPtr != nullptr && fastPtr->next != nullptr)
{
fastPtr = fastPtr->next->next;
slowPtr = slowPtr->next;
if(fastPtr == slowPtr)break;
}
if(fastPtr == nullptr || fastPtr->next == nullptr)return nullptr;
fastPtr = pHead;
while (fastPtr != slowPtr)
{
fastPtr = fastPtr->next;
slowPtr = slowPtr->next;
}
return fastPtr;
}
};