首先是141题,如何判断一个链表是否有环,题目如下所示:
拿到这个题目,我们很难想到什么比较简洁快速的解法,的确这个题目用常规的方法比较难解决。
在这里给大家介绍一个快慢指针的解法,首先我们定义两个指针 fast 和 slow ,分别表示快指针和慢指针,快指针是每一次走两步,慢指针是每一次走一步。那么如果一个链表中存在环的话,那么这个快指针一定会比慢指针先进环中,然后慢指针也会进到换中,因为快指针的速度比慢指针步长大,那么在环中快指针一定会在环中追上慢指针,如果在链表结束前快慢指针相遇,那么链表中就存在环。这个逻辑是比较容易理解的,但是比较难想到,代码如下所示:
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def hasCycle(self, head):
"""
:type head: ListNode
:rtype: bool
"""
# 定义快慢指针初始位置
fast = slow = head
# 如果快慢指针相遇,那么链表中存在环
while slow and fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
return True
return False
在这道题之后,有一道关于这道题的进阶,就是需要找到开始入环的第一个节点,题目如下所示:
有了上面的基础,我们只需要考虑如何找到环的入口。首先需要考虑的是快慢指针所走过的距离,如下图所示:
下面高潮来了,注意看:
在上图中,
A
A
A 点是我们需要找到的链表中环的入口,
B
B
B 点表示两个指针在环中相遇的位置,那么在
B
B
B 点的时刻慢指针所走的路程为 红色的部分
a
a
a 加上绿色的部分
b
b
b,那就是
a
+
b
a+b
a+b;快指针因为速度比慢指针要快,那么快的指针可能在圈里循环了
k
k
k 圈,
k
=
1
,
2
,
3
,
.
.
.
,
n
k = 1,2,3,...,n
k=1,2,3,...,n,那么快指针走过的路程为:
a
+
b
+
k
⋅
(
b
+
c
)
a + b + k \cdot (b + c)
a+b+k⋅(b+c),这么说没毛病吧,很容易理解吧?然后因为快指针每次走两步,慢指针每次走一步,那么快指针走过的路程是慢指针的两倍,那么用数学表达式可以表示成:
a
+
b
+
k
⋅
(
b
+
c
)
=
2
(
a
+
b
)
;
k
=
1
,
2
,
3
,
.
.
.
,
n
a + b + k \cdot (b + c) = 2(a + b);\quad k=1,2,3,...,n
a+b+k⋅(b+c)=2(a+b);k=1,2,3,...,n
然后我们由次表达式可以得到:
a
=
k
⋅
(
b
+
c
)
−
b
;
k
=
1
,
2
,
3
,
.
.
.
,
n
a = k \cdot (b + c) - b; \quad k=1,2,3,...,n
a=k⋅(b+c)−b;k=1,2,3,...,n
我们最终就是要求
a
a
a,因为
a
a
a 的距离就表示了环的入口,我们再将上述表达式稍作变换:
a
=
(
k
−
1
)
⋅
(
b
+
c
)
+
c
;
k
=
1
,
2
,
3
,
.
.
.
,
n
a = (k - 1) \cdot (b + c) + c; \quad k = 1,2,3,...,n
a=(k−1)⋅(b+c)+c;k=1,2,3,...,n
所以
a
a
a 的距离等于
k
−
1
k-1
k−1个圈的距离加上
c
c
c,所以我们再拿两个指针,一个从
A
A
A 点出发,一个从
B
B
B 点出发,步长都为1,那么最终两个指针相遇的节点一定是
A
A
A,这个没毛病吧?这样就可以求出链表中环的入口了,下面上代码:
# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution(object):
def detectCycle(self, head):
# 判断链表中是否有环
fast = slow = head
hasCycle = False
while slow and fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast:
hasCycle = True
break
if not hasCycle:
return None
# slow 指针从A点出发,fast指针继续从B出发,步长为1
slow = head
if hasCycle:
while slow is not fast:
slow = slow.next
fast = fast.next
return slow
是不是有种豁然开朗的感觉,好了,就到这里,希望本文对大家对链表的理解更深了一个层次,谢谢。