题目说明
你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
示意图:
方法一
思路:
如果链表中没有环的话我们遍历完整个链表每个节点只会被访问一次,相反如果链表中有环一次会存在某个节点重复访问的情况。
因此我们用一个hash表记录访问过的链表,当有重复时就代表链表中存在环。
public boolean hasCycle(ListNode head) {
/*
* 用一个哈希表记录访问过的节点,当链表节点重复时代表当前的链中存在环
*/
if(head==null){
return false;
}
ListNode p = head;
Set<ListNode> set = new HashSet<>();
while(p!=null){
if(!set.add(p)){
return true;
}
p = p.next;
}
return false;
}
算法分析
我们最多循环完整个链表就可以知道链表中是否存在环,因此该算法的时间复杂度为O(n)
空间复杂度,我们需要用哈希表来存储访问过的节点,同样的也是最多储存n个节点,空间复杂度为O(n)
方法二
思路:
1.声明一快一慢两个指针,慢指针每次移动一步,快指针每次移动两步。如果链表中有环,快指针和慢指针一定会在k步内相遇。
证明:
证明慢指针进入环一圈内会和快指针相遇
设环的长度为C,
假设当慢指针进入环的入口时快指针的位置为A,
设环入口的位置为0
则A的取值范围为[0,C-1]
假设慢指针移动k步后和快指针相遇,慢指针跑出距离0+k,快指针跑出距离2k+A
此时快指针比慢指针要绕环多跑n圈(从慢指针入环后开始计数)
我们有 2k+A - (k+0) = nC 简化得 k + A = nC
当n取值为1时,可得出k一定有解,且k的取值范围为 [0,C-1]
特殊情况:当A的值为0时,此时慢指针刚进入环就和快指针相遇此时k为0,此时n取值0
示意图:
import java.util.HashSet;
/**
* 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||head.next==null){
return false;
}
ListNode slow = head;
ListNode fast = head;
do {
if(fast==null||fast.next==null){
return false;
}
slow=slow.next;
fast=fast.next.next;
} while(fast!=slow);
return true;
}
}
算法分析
如果链表中没有环我们最多循环完整个链表就可以知道链表中是否存在环,存在环的情况下快慢指针一定会在环的第一圈内相遇,因此该算法的时间复杂度为O(n)
我们值需要常量的空间来存储额外的变量,空间复杂度为O(1)