判断是否链表有环
-
方案一:
遍历链表,每到一个新节点,便通过检查前面所有节点查看是否以已经访问过了,有则是有环。时间复杂度 $o(n^2) $
-
方案二:
使用HashSet存储访问过的节点ID 。时间复杂度o(n) 空间复杂度o(n)
3. 方案三:快慢指针
创建两个指针 p1和p2,p1每次移动一个节点,p2每次移动两个节点,然后比较p1和p2是否相同,相同则有环,否则继续移动
时间复杂度O(n)
有环一定会追上?
两个 速度不同的远动员在环形跑道上不停的跑,一定会相遇
实现
给出node判断是否有环
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public boolean hasCycle(ListNode head) {
if(head==null)
return false;
ListNode p1=head;
ListNode p2=head;
while(p2.next!=null){
p1=p1.next;
p2=p2.next;
if(p2.next==null){
break;
}
p2=p2.next;
if(p1==p2)
return true;
}
return false;
}
}
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
使用hashset方法 能够很轻松的找到
如何使用快慢指针呢?
设 链表长度为n, 头节点坐标为0
设环长为l, t=n-l 为起始点到入环节点的距离
遍历过的节点个数 n 1 , n 2 n_1,n_2 n1,n2分别为 p 1 , p 2 p_1,p_2 p1,p2走过的距离, 2 ∗ n 1 = n 2 2*n_1=n_2 2∗n1=n2
当节点相遇时p2比p1多跑了 k圈: n 2 − n 1 = k ∗ l = n 1 , 又 n 1 = ( k − 1 ) ∗ l + t + x n_2-n_1=k*l = n_1,又 n_1=(k-1) * l +t+x n2−n1=k∗l=n1,又n1=(k−1)∗l+t+x x为相遇节点于入环节点的距离
可知 t + x = l t+x=l t+x=l
则从相遇节点再走t步即可到达环节点
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head==null)
return null;
ListNode p1=head;
ListNode p2=head;
while(p2.next!=null){
p1=p1.next;
p2=p2.next;
if(p2.next==null){
break;
}
p2=p2.next;
if(p1==p2){
p2=head;
while(p1!=p2){
p1=p1.next;
p2=p2.next;
}
return p1;
}
}
return null;
}
}
应用3求环的长度
当两个节点第一次相遇后,再让他们继续前进,再次相遇就能求出环长