1.单向链表中环的问题
力扣141,给定一个链表,判断单向链表中有无环,进一步确定环的入口。
1.1 第一种方法——HashMap
第一种方法是利用HashMap
来解决,遍历链表时将元素加入map中,要是有环存在一定会发生碰撞,发生碰撞的位置也就是环的入口位置。
var detectCycle = function(head) {
let pos = head;
const nodeMap= new Map();
while (pos) {
if (nodeMap.has(pos)) return pos;
else {
nodeMap.set(pos, true); // 存的是结点的地址引用而非节点值
pos = pos.next;
}
}
return null;
}
1.2 第二种方法——快慢指针法
确定是否有环存在最有效的方法就是快慢指针,那么快慢指针为什么一定会相遇呢?
这就像两个人在操场跑圈一样,一个人跑的快另外一个人跑得慢,不管这两个人的跑步速度是多少,跑得快的那个一定会遇见跑得慢的那个人!
如图一个环形链表,快慢指针相遇在y
点后,快指针回到起点,快慢指针以相同速度推进,会在环的入口x
位置相遇。
①假如跑一圈就相遇
让快指针走两步,慢指针走一步,那么第一次相遇时,快指针走了a+b+c+b
步,慢指针走了a+b
步,根据等式关系
2
(
a
+
b
)
=
a
+
b
+
c
+
b
2(a+b)=a+b+c+b
2(a+b)=a+b+c+b可得
a
=
c
a=c
a=c,此时让快指针回到起点,快慢指针同时走,相遇的位置就是环的入口。
②假如跑n
圈才相遇
相遇时快指针走了a+n(b+c)+b
步,慢指针走了a+b
步,根据等式关系
2
(
a
+
b
)
=
a
+
n
(
b
+
c
)
+
b
2(a+b) = a+n(b+c)+b
2(a+b)=a+n(b+c)+b 可得
a
=
c
+
(
n
−
1
)
(
b
+
c
)
a=c+(n-1)(b+c)
a=c+(n−1)(b+c) 即
a
=
c
+
(
n
−
1
)
l
e
n
g
t
h
O
f
C
i
r
l
c
e
a=c+(n-1)lengthOfCirlce
a=c+(n−1)lengthOfCirlce ,这说明当快慢指针相遇时,快指针已经在环里溜达很多圈儿了。
找到环形链表的入口可以这样做:
// 双指针方法找到环形链表的入口
var findEnterOfCircle = function(head) {
if (!head || !head.next) return null;
let fastPointer = slowPointer = head;
while (fastPointer && fastPointer.next) {
fastPointer = fastPointer.next.next;
slowPointer = slowPointer.next;
// 如果快慢指针相遇,那么让快指针回到起点,两个指针同时向前走,再相遇时就是环形链表入口位置
if (fastPointer === slowPointer) {
fastPointer = head;
while (fastPointer !== slowPointer) {
fastPointer = fastPointer.next;
slowPointer = slowPointer.next;
}
return fastPointer;
}
}
return null;
}
第二种确认入口的方法如下,也是双指针,只不过用了三次。
// 三次双指针方法
/*思想:
第一次确定是否存在环,
第二次找到环的长度,让快指针固定在相遇位置,慢指针继续向前走,再相遇时就视为一次遍历得到环的长度
第三次通过找倒数第K个结点的方法来找入口,先让快指针走K步,再让两个指针同时走,当快指针来到相遇点时,慢指针所指位置就是入口
*/
var findEnterOfCircle = function(head) {
// 第一次双指针
if (!head || !head.next ) return null;
let fastPointer = slowPointer = head;
let lengthOfCircle = 0;
while (fastPointer && fastPointer.next) {
fastPointer = fastPointer.next.next;
slowPointer = slowPointer.next;
// 如果两个指针相遇了,说明有环
if (fastPointer === slowPointer) {
// 开始第二次双指针,得到环的长度
slowPointer = slowPointer.next;
while (slowPointer !== fastPointer) {
++lengthOfCircle;
slowPointer = slowPointer.next;
}
}
// 开始第三次双指针
for (let i = 1; i > 0 ; i--) {
fastPointer = fastPointer.next;
}
while (fastPointer !== slowPointer) {
fastPointer = fastPointer.next;
slowPointer = slowPointer.next;
}
return slowPointer;
}
return null;
}