前言
今天说链表算法题的最后一题:环的检测
- 单链表反转
- 两个有序的链表合并
- 删除链表倒数第n个结点
- 求链表的中间结点
- 链表中环的检测
题目:链表中环的检测
给定一个链表,判断链表中是否有环
。
如果链表中有某个节点,可以通过连续跟踪 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)