前言
题目来源于牛客网
描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围: n≤10000n\le10000n≤10000,
1<=结点值<=100001<=结点值<=100001<=结点值<=10000
要求:空间复杂度 O(1)O(1)O(1),时间复杂度 O(n)O(n)O(n)
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。
输入描述
:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述
:
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
示例1
输入:
{1,2},{3,4,5}
返回值:
3
说明:
返回环形链表入口结点,我们后台程序会打印该环形链表入口结点对应的结点值,即3
示例2
输入:
{1},{}
返回值:
“null”
说明:
没有环,返回对应编程语言的空结点,后台程序会打印"null"
示例3
输入:
{},{2}
返回值:
2
说明:
环的部分只有一个结点,所以返回该环形链表入口结点,后台程序打印该结点对应的结点值,即2
方法一——使用STL容器,set集合
关于set集合的详细介绍,我在算法入门到进阶的博客中有写过,大家可以往回看一下
。这里补充一个set的知识点。
set.find(k)
:表示查找set集合当中的k元素,找到了返回一个迭代器指向该元素,如果没有找到就返回集合中最后一个元素之后的位置。
set.end()
:表示set集合最后一个元素后的位置
源码
class Solution{
public:
ListNode* EntryNodeOfLoop(ListNode* pHead){
unordered_set<LisNode *>s;
while(pHead){
if(s.find(pHead)==s.end()){//没有找到pHead
s.insert(pHead);//将phead所指向的值加入到set集合中
pHead=pHead->next;
}else{
return pHead; //如果找到了,那这个值就是入口点
}
}
return NULL;//表示没有入口点
}
};
通过测试
(有点大,不过没关系,肯定可以表达您明白了如何解决该类题的心情)
方法二——快慢指针
思路
快慢指针的思路,就好像两个小朋友。小朋友A的速度是B的两倍(快指针一次比慢指针多指向一倍的距离跳度)。
如果没有环,则快指针会指向NULL,
如果有环,则当两者第一次相遇,表示快指针跑了慢指针两倍的距离,且经历的轨迹会是环的周长的n倍。因此,当两者相遇时,让快指针指向头节点且和慢指针的速度改为一样,则下次的相遇点就是入口点
由上图可得:
快指针: A-B-C-D-B-C
慢指针:A-B-C
因此 A-B-C=C-D-B-C
同时去除公共部分 B-C,可得当慢指针从C出发,快指针从A出发,以相同的速度的相聚点就是环的入口结点。
源码
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead) {
ListNode* fast=pHead;
ListNode* slow=pHead;
while(fast && slow && slow->next){
fast=fast->next->next;
slow=slow->next;
if(fast==slow){//第一次相遇
fast=pHead;
while(1){ //相遇过就表示有环了
if(fast==slow){
return fast;
}
fast=fast->next;
slow=slow->next;
}
}
}
return nullptr;
}
};