给你一个链表的头节点 head
,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos
不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true
。 否则,返回 false
。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1 输出:false 解释:链表中没有环。
提示:
- 链表中节点的数目范围是
[0, 104]
-105 <= Node.val <= 105
pos
为-1
或者链表中的一个 有效索引 。
思路:
一种简单的方法,用set判断有没有重复。不过时间和空间复杂度都是O(n)
如果要空间复杂度O(1)的话,可以用快慢指针。
贴一下两种方法的代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
set<ListNode*> s;
ListNode* p=head;
int t1,t2;
while(p){
t1=s.size();
s.insert(p);
t2=s.size();
if(t1==t2)return true;
p=p->next;
}
return false;
}
};
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL||head->next==NULL)return false;
set<ListNode*> s;
ListNode* p=head;
ListNode* p2=head->next;
while(p&&p2){
if(p==p2)return true;
p=p->next;
if(p2->next)p2=p2->next;
else return false;
if(p2->next)p2=p2->next;
else return false;
}
return false;
}
};
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0 输出:返回索引为 0 的链表节点 解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1 输出:返回 null 解释:链表中没有环。
提示:
- 链表中节点的数目范围在范围
[0, 104]
内 -105 <= Node.val <= 105
pos
的值为-1
或者链表中的一个有效索引
进阶:你是否可以使用 O(1)
空间解决此题?
思路:
还是用快慢指针。
按照上一题,有环的话,快慢指针一定会相遇。所以我们只要在此基础上去找环的入口就好了。
假设链表长度是a+b,a指的是非环区域,b是环的长度。
那么在指针相遇的时候,p2(快指针)的步数一定是p(慢指针)步数的两倍:p2=2*p
而他们相遇是因为p2比p多走了好几个环:p2=p+nb
两个式子相减可得:p=nb,p2=2nb,也就是两个指针分别走了n个环长和2n个环长。
链表头在哪里?按照上面说的a是非环区域,所以a+nb都是走到链表头部。
而p已经走了nb步了,所以p再走a步就可以到了。
那怎么走a步呢?让p2回到起点开始走a步,当p2走a步时,p走了a+nb步,刚好在入口相遇。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL||head->next==NULL)return NULL;
set<ListNode*> s;
ListNode* p=head;
ListNode* p2=head;
while(p&&p2){
p=p->next;
if(p2->next)p2=p2->next;
else return NULL;
if(p2->next)p2=p2->next;
else return NULL;
if(p==p2){
p2=head;
while(p!=p2)
{
p=p->next;
p2=p2->next;
}
return p;
}
}
return NULL;
}
};