题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路
思路1:有环意味着遍历的时候在一定的时间之后,会出现重复的指针。因此我们可以用一个map来存储出现的次数,一旦发现某个结点已经出现过了,即次数大于了1,立即输出;否则等遍历到底就说明是没有环的。
实现:
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
map<ListNode*,int> m;
//unordered_map<ListNode*,int> m;
//用unordered_map性能会更好,在插入和查找的复杂度都比map低,并且空间复杂度也比map低
while (pHead)
{
m[pHead]++;
if (m[pHead]>1) return pHead;
pHead=pHead->next;
}
return nullptr;
}
};
用set会获得更好的效果,因为set只有关键字而没有关键字的值,相当于省了一半的空间
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
unordered_set<ListNode*> s;
while (pHead)
{
if (s.find(pHead)==s.end())//这里的find返回的是一个迭代器,如果迭代器指向了set的末尾,说明没有找到pHead,接下来就插入和后移。
{
s.insert(pHead);
pHead=pHead->next;
}
else
return pHead;
}
return nullptr;
}
};
关于set和unordered_set的知识:
C++中set和unordered容器的使用
思路2:双指针法(快慢指针)
具体操作:
1.初始化两根指针fast和slow指向链表头结点
2.快指针每次走2步,慢指针每次走1步,当第一次相遇时停下来
3.再让快指针回到起点,慢指针不动,一起每次走一步,再次相遇即入口结点
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
ListNode* fast=pHead;
ListNode* slow=pHead;
while (fast&&fast->next)
{
fast=fast->next->next;
slow=slow->next;
if (slow==fast) break;//第一次相遇
}
if (!fast||!fast->next) return nullptr;//检查上面的while退出是因为fast或者fast->next为空退出(即没有环)还是因为fast和slow指针相遇退出的
fast=pHead;//将快指针重新指向头结点
while (fast!=slow)
{
fast=fast->next;//一起每次走一步
slow=slow->next;
}
return fast;
}
};