「这是我参与11月更文挑战的第 1 天,活动详情查看:2021最后一次更文挑战」
题目来源:LeetCode-141. 环形链表
题目
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 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
解释:链表中没有环。
题解
提出问题
- 如何证明链表中有环?
分析
- 当链表中存在一个节点,可以通过连续跟踪
next指针再次到达,则证明链表中存在环。
解法
- 通过遍历链表方式把每一个节点都通过
set方式到Map对象中; set之前先通过has方式判断Map对象中是否存在该结点,如果存在,则为有环;- 当遍历完成也就是
next指针指向null的话,则为无环。
代码实现
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
let pointer = head;
let map = new Map();
while (pointer !== null) {
if (map.has(pointer)) {
return true;
} else {
map.set(pointer);
pointer = pointer.next;
}
}
return false;
};
空间复杂度
上述代码中的Map对象由于Map对象所占用的空间是受到链表影响的空间复杂度为O(N)。
空间复杂度
上述代码中通过while遍历链表是受到链表长度影响的时间复杂度为O(N)。
进阶
你能用 O(1) (即,常量)内存解决此问题吗?
提出问题
- 如何通过不存储链表节点来证明链表中有环?
分析
- 可以通过快慢指针(快指针每次走两步,慢指针每次走一步);
- 当快指针的下一位仍有数值时,通过
while继续移动快慢指针; - 如果快指针和慢指针相遇(重合),说明该链表是有环链表,返回True;
- 如果快指针走到链表尾仍没有遇到慢指针,说明是无环链表,返回False。
代码实现
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
let fast = head;
let slow = head;
while(fast&&slow&&fast.next){
fast = fast.next.next;
slow = slow.next;
if(slow == fast)return true
}
return false;
};
趣味实验
分析
- 遍历有环的链表会一直执行下去(死循环)
- 把一个死循环的链表对象转换JSON字符串会报错
无环链表
有环链表
通过上述实验可以证明有环的链表转换成JSON对象会报错
- 可以通过
try catch来捕获错来判断是否有环
代码实现
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
try {
JSON.stringify(head);
return false;
} catch (err) {
return true;
}
};