[剑指offer] 23. 链表中环的入口结点

使用快慢指针法解决链表中环的入口节点问题。先判断链表是否存在环,若存在,则通过快慢指针从链表头和相遇点同时开始,相遇点即为环的入口。
摘要由CSDN通过智能技术生成

题目

给定一个链表,若其中包含环,则输出环的入口节点。若其中不包含环,则输出null。
在这里插入图片描述

给定如上所示的链表:
[1, 2, 3, 4, 5, 6]
2
则输出环的入口节点3.
注意,样例里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。输出3。

思路

使用快慢指针法。有两种做法,推荐第一种。

Ps:链表之所以为链,就是因为指针。所以链表的题解本质就是对指针的各种操作:双指针,快慢指针,周期性……

算法1(推荐)

【1】判断是否有环。设置快慢指针,快指针每次走两步,慢指针每次走一步,若快慢指针相遇,则说明该链表包含环。在相遇点处停止快慢指针,并记录相遇点(在该处保留慢指针)。若快指针已经走完整个链表(走到None)仍然没有相遇过,说明该链表没有环。
【2】找出环的入口。设置两个指针,一个指针位于链表头,一个指针位于相遇点,同时开始,每次都走1步,相遇时,相遇点即为环的入口。(将快指针重置在链表头,与相遇点处保留的慢指针一起移动,步长均为1,直到相遇)

在这里插入图片描述
证明:

如上图所示,a 是起点,b 是环的入口,c 是两个指针的第一次相遇点,ab 之间的距离是 x,bc 之间的距离是 y。 则当 slow 走到 b 时,由于 fast 比 slow 多走一倍的路,所以 fast 已经从 b 开始在环上走了 x 步,可能多余1圈,距离
b 还差 y 步(这是因为第一次相遇点在 b 之后 y 步,我们让 slow 退 y 步回到 b 点,则 fast 会退 2y
步,也就是距离 b 点还差 y 步);所以 fast 从 b 点走 x+y 步即可回到 b 点,所以 fast 从 c 点开始走,走 x
步即可恰好走到 b 点,同时让 slow 从头开始走,走 x 步也恰好可以走到 b 点。所以第二次相遇点就是 b 点。

另一种思路,可以用公式来说明:a,b,c,x,y 的含义同上,我们用 z 表示从 c 点顺时针走到 b 的距离。则第一次相遇时 fast
所走的距离是 x+(y+z)∗n+y , n 表示圈数,同时 fast 走过的距离是 slow 的两倍,也就是 2(x+y),所以我们有
x+(y+z)∗n+y=2(x+y),所以 x=(n−1)×(y+z)+z,n>=1。n=1的时候,也就是fast比slow多走一圈相遇,此时有x==z,也就是
如果fast和slow以相同速度分别从a和c出发,他们将会在b再次相遇,即为所求。

时间复杂度
slow 总共走了 2x+y 步,fast 总共走了 2x+2y+x 步,所以两个指针总共走了 5x+3y 步。由于当第一次 slow 走到 b 点时,fast 最多追一圈即可追上 slow,所以 y 小于环的长度,所以 x+y 小于等于链表总长度。所以总时间复杂度是 O(n)。

代码

Tips:
while循环结束后直接return,同时包含了break+程序末尾return的功能,推荐使用
否则,这里需要设置tag,以便在程序末尾分情况讨论return

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def entryNodeOfLoop(self, head):
        """
        :type head: ListNode
        :rtype: ListNode
        """
        if head==None or head.next==None:
            return None
            
        f=head
        s=head
        #tag=False
        
        while f and f.next:
            f=f.next.next
            s=s.next
            if f==s:
                #tag=True
                f=head
                while s!=f:
                    f=f.next
                    s=s.next
                #break
                return f       ## 推荐这种返回方式
        return None
        #return f if tag else None     

算法2

【1】判断是否有环。设置快慢指针,快指针每次走两步,慢指针每次走一步,若快慢指针相遇,则说明该链表包含环。停止快慢指针,记录相遇结点。
【2】判断环内有多少个结点。设置一个指针指向相遇点,另一个从相遇点开始走,如果再次回到相遇点,则说明已经走了一圈,可得到环内的结点数count。
【3】找出环的入口。设置两个指针指向链表头,其中一个指针先走count步,然后同时开始走,若两个指针相遇,相遇点即为环的入口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值