141. 环形链表
一.题目
1.1题目描述
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
1.2 示例
1.3题目提示
链表中节点的数目范围是 [0, 104]
-105 <= Node.val <= 105
pos 为 -1 或者链表中的一个 有效索引 。
二.题解
由于链表只能单向遍历,环形链表的结尾无法判断,造成了不小的难度。
2.1辅助指针数组
2.1.1思路
一个环形链表,长度为length,最后一个节点经length次迭代,会回到环形圈内,需要设计一个方法来比较已走过的节点和迭代测试走过的节点。
这里为了理清思路,假设圈子内有CircleLen个节点,已走过length个节点(包括将要判断的当前节点)
圈内节点经length步回到原位,那么只要length%CirclLen==0就能判断为环形。设0为当前指针的位置,我们要判断它是否迭代进环中。
可以看出,对环起始点处的长度length*,
只要存在
0
<
=
n
<
C
i
r
c
l
e
L
e
n
使
得
0<=n<CircleLen使得
0<=n<CircleLen使得(length*+n)%(CircleLen)==0
那么就可以断定是个环形链表。
从n的范围很容易看出一定有n使之成立。而
l
e
n
g
t
h
∗
+
n
length*+n
length∗+n实际上就是圈子里的第n-1个节点的length。
所以只要遍历到某个节点length%(CircleLen)==0,我们就可以断言它是环形链表。
2.1.2复杂度分析
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)对第length个节点最多需要length次操作。
空间复杂度:
O
(
1
)
O(1)
O(1)未调用局部内存空间。
2.1.2代码实现
/**
* 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) {
vector<ListNode*> pointers(NULL);
ListNode*temp(NULL);
int i(0),length(0);
while(head!=NULL)
{
pointers.push_back(head);
temp=head;
for(i=0;i<length&&temp!=NULL;i++)
{
if(temp==pointers[i])
return true;
else
temp=temp->next;
}
head=head->next;
++length;
}
return false;
}
};
2.2切断法
2.2.1思路
我考虑过用NULL切断,迭代它原本的next节点找爸爸的方法,想到可能死循环考虑使用递归,但找不到判断结尾的方法。浏览题解发现用自己的next切断,用自己的next找爸爸的神奇方法。(大概是昨天调试的错误让我默认避免自己指自己的节点)
2.2.2复杂度分析(两种实现方法)
时间复杂度: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) {
ListNode *cut;
while(head!=NULL&&head->next!=NULL)
{
if(head->next==head->next->next)
return true;
cut=head;
head=head->next;
cut->next=cut;
}
return false;
}
};
使用递归
class Solution {
public :bool hasCycle(ListNode* head) {
//结尾判断
if (head == NULL || head->next == NULL)
return false;
if (head->next == head)
return true;
//归纳
ListNode* nextNode = head->next;
head->next = head;
return hasCycle(nextNode);
}
};
2.3快慢指针
2.3.1思路
环形赛道追及问题
设两指针速度v1,v2且v1<v2
设进入跑道前有m个节点
跑道有n个节点,经时间t,k圈两针相遇
t
=
(
m
+
k
n
)
/
v
2
−
v
1
t=(m+kn)/v2-v1
t=(m+kn)/v2−v1
2.3.2复杂度
时间:O(n)
空间O(1)
2.3.3代码
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL)
return false;
ListNode *v1(head),*v2(head->next);
while(v2!=NULL&&v2->next!=NULL)
{
if(v2==v1)
return true;
else
{ v1=v1->next;
v2=v2->next->next;
}
}
return false;
}
};