- 环形链表
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 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)(即,常量)内存解决此问题吗?
解题思路:这里学习了floyd判圈算法,也称龟兔赛跑算法
思路就是设置两个指针,一个跑的快,一个跑的慢,都从起点开始,跑的快的每次两步,跑的慢的每次一步,如果有环他们迟早会相遇的。
这个思想也能用来求出环的长度:
当相遇了之后,继续让跑得慢的走,就是那个每次一步的继续走,等到再次相遇,走的步数就是环的长度。
上面两种在网上资料中的解释:
判断是否存在环路:
如果有限状态机、迭代函数或者链表存在环,那么一定存在一个起点可以到达某个环的某处(这个起点也可以在某个环上)。
初始状态下,假设已知某个起点节点为节点S。现设两个指针t和h,将它们均指向S。接着,同时让t和h往前推进,但是二者的速度不同:t每前进1步,h前进2步。只要二者都可以前进而且没有相遇,就如此保持二者的推进。当h无法前进,即到达某个没有后继的节点时,就可以确定从S出发不会遇到环。反之当t与h再次相遇时,就可以确定从S出发一定会进入某个环,设其为环C。如果确定了存在某个环,就可以求此环的起点与长度。
求解环路的长度:
上述算法刚判断出存在环C时,显然t和h位于同一节点,设其为节点M。显然,仅需令h不动,而t不断推进,最终又会返回节点M,统计这一次t推进的步数,显然这就是环C的长度。
还有个更强的:能退着求出这个环的起点。
网上资料中的解释:
为了求出环C的起点,只要令h仍均位于节点M,而令t返回起点节点S,此时h与t之间距为环C长度的整数倍。随后,同时让t和h往前推进,且保持二者的速度相同:t每前进1步,h前进1步。持续该过程直至t与h再一次相遇,设此次相遇时位于同一节点P,则节点P即为从节点S出发所到达的环C的第一个节点,即环C的一个起点。
简单翻译:就是当快慢指针相遇的时候,他们是可能相遇在环上的某个点,而不是这个环的起点,所以这样怎么算哪个是点是起点呢?可以多画图测试一下理解一下,这个时候让一个指针回到整个遍历起始的节点,这两个指针之间必然是环长度的整数倍,然后同时走一步进行推进,能在第一个节点相遇。
可以参考这个图多画画试试或者自行想象
解题思路2:也可以考虑到JSON.stringfy的不能序列化含有循环引用的结构
通常,我们会用JSON.stringify把Javascript对象序列化成JSON格式,这在大多数情况下是够用的。但是,当你要转换的对象里存在循环引用时,就会报错了
所以思路就是直接用JSON.stringfy把这个指针对象转换成字符串,如果成功运行了就是没有环,如果运行出错,就是有环,这个有没有成功运行的判断,可以用到try+catch
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @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;
};
var hasCycle = function(head) {
try{
JSON.stringify(head);
return false;
}
catch(err){
return true;
}
};