今天说链表算法题的最后一题:环的检测
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 pos为环的起点位置。
如果链表中存在环,则返回 true 。否则,返回 false 。
进阶:你能用 O(1)(即,常量)内存解决此问题吗?
示例 1:

输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:输入:head = [1], pos = -1 输出:false 解释:链表中没有环。
题目比较长,意思其实很简单,就是同一个结点会不会被两个不同结点所连接,反应到链表就是:
是否有两个结点的next都指向同一个结点,如果有,那就代表链表中有环型结构。
所以我们遍历链表,然后将链表的每个结点存储起来,如果发现有重复就代表有环:
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> nodeSet = new HashSet<ListNode>();
while (head != null) {
if (!nodeSet.add(head)) {
return true;
}
head = head.next;
}
return false;
}
}
时间复杂度为O(n)
空间复杂度为O(n)
题目有提示,是否可以有空间复杂度为O(1)的解法呢?
快慢指针,没错,又是快慢指针。
快指针每次走两步,慢指针每次走一步,正常情况下,没有环的话,两者是不会相遇的。
如果有环结构,那么在环里面,如果快慢指针之间的距离为X,那么每走一步,快指针和慢指针之间的距离都会-1,所以总会有一个时刻,他们会相遇。
所以只要发现有相遇的情况,就证明该链表有环。
这种算法也叫做龟兔赛跑算法。
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
return true;
}
}
return false;
}
}
其实关于龟兔赛跑算法还有很多问法,比如如何确认环的起点,如何确认环的长度等等,下次再详细聊聊。
时间复杂度为O(n)
只用两个额外的指针,所以空间复杂度为O(1)
https://leetcode-cn.com/problems/linked-list-cycle/
感谢大家的阅读,有一起学习的小伙伴可以关注下公众号—
码上积木❤️❤️ 每日三问知识点/面试题,积少成多。