给你一个链表的头节点
head,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪
next指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos不作为参数进行传递 。仅仅是为了标识链表的实际情况。如果链表中存在环 ,则返回
true。 否则,返回false。
解法1 暴力解法
思路
暴力解法的思路依然是将链表用哈希表缓存下来。
如果当前节点存在于哈希表当中,则直接返回。反之则加入哈希表,然后继续遍历下一个节点。
当存在环时,会在哈希表当中查询到直接返回;当没有环时,当前遍历节点为 null,退出循环。
代码
function hasCycle(head: ListNode | null): boolean {
if (!head || !head.next) {
return false;
}
const set = new Set();
let cur = head;
while (cur) {
if (set.has(cur)) {
return true;
} else {
set.add(cur);
}
cur = cur.next;
}
return false;
};
时空复杂度分析
时间复杂度:需要遍历所有节点 O(n)
空间复杂度:额外哈希表空间开销 最差缓存所有节点 O(n)
解法2 快慢指针
思路
针对于空间上的优化,一般来说采用多指针的方式,而对于判断环形来说,可以采用快慢指针来判定。
如果不存在环形,那么快指针会走到 null 此时,退出循环,判定为无环。
如果存在环形,那么快慢指针会在环形内形成追击,总会在某一节点相遇。快指针一直走在慢指针前面,如果相遇了,那刚好也证明了是存在环形的,要不然不会相遇。
代码
function hasCycle(head: ListNode | null): boolean {
if (!head || !head.next) {
return false;
}
let fast = head;
let slow = head;
while (fast !== null && fast.next !== null) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) {
return true;
}
}
return false;
};
时空复杂度
时间复杂度:最坏情况是在环内追击走2倍长度,渐进复杂度为 O(n)
空间复杂度:常数变量 O(1)