1. 题目描述
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?
2. 解题方法及代码
2.1 存储法
简而言之,就是把链表中的所有节点都存储进一个容器中,每次比较当前节点是否与容器中节点相同,如果相同则说明出现了环。
我采用的容器是ArrayList,后面对照别人的题解时,发现如果采用HashSet,代码会显得更加简单(因为存储的已有节点不需要有序)。
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode curr = head;
boolean cycleflag = false;
ArrayList<ListNode> list = new ArrayList<>();
while (curr != null) {
for (ListNode s : list) {
if (s == curr) {
cycleflag = true;
break;
}
}
if (!cycleflag) {
list.add(curr);
} else break;
curr = curr.next;
}
return cycleflag;
}
}
因为没有确定ArrayList的容量,ArrayList需要频繁扩容,很浪费时间和内存,且ArrayList查找需要遍历整个列表。
时间复杂度为O(n^2),空间复杂度为O(n)。
LeetCode提交结果:92 ms 37.8 MB
2.2 快慢指针法
设置快慢指针,将两个指针想象成两个在环形赛道上不停赛跑的运动员,跑的快的人最终会在某一个时刻追上跑得慢的人并相遇。因此,如果两个指针在某一个时刻指向同一块地址,说明环存在,如果跑得快的指针指向了null,说明没有环存在。
public class Solution {
public boolean hasCycle(ListNode head) {
if (head != null){
ListNode slow = head; // 慢指针从链表头开始,每次跳一步
if (head.next != null && head.next.next != null) {
ListNode fast = head.next.next; // 快指针从链表第3个节点开始,每次跳2步
while (fast.next != null && fast.next.next != null) {
if (fast == slow) return true; // 快指针与慢指针相遇,说明有环
fast = fast.next.next;
slow = slow.next;
}
}
}
return false; //如果快指针的下一跳为null,说明没有环
}
}
时间复杂度O(n),空间复杂度O(1)。
LeetCode提交结果: 0 ms 37.6 MB