链表中环的入口节点

链表中环的入口节点

题目描述:如果一个链表中包含环,如何找出环的入口节点?如图所示,环的入口节点是节点3。
示例图
首先第一步,我们应该确定链表中是否包含环。可以使用快慢指针来解决这个问题,定义两个指针,同时从链表的头节点出发,快指针一次走两步,慢指针一次走一步,如果快指针走到了链表的末尾(NULL)说明链表是无环的,因为如果有环的话最后的结点不会是NULL而是环的开始。如果快指针追上慢指针,说明链表有环。
这里如果链表有环,快指针一定是能够追到慢指针的。假设快指针走后,距离慢指针距离为n,那么因为链表有环(可以理解为操场跑圈),快指针每走一步会经过两个节点,而慢指针只会经过一个节点,因此快指针每走一步与慢指针的距离都会-1,最终的距离为 n − 1 , n − 2 , ⋯   , 2 , 1 , 0 n-1,n-2,\cdots,2,1,0 n1,n2,,2,1,0。因为距离每次会减一,所以最终会到0,即二者相遇。
然后就是要找到环的入口。这里还是用两个指针来解决问题。我们首先思考一下,如果链表的总长度为n,那么指向链表头节点的指针走过n-1步就到达链表的末尾(以上图为例,从1号节点出发,走5步可以到6号节点)那么再走一步就到达了环的开头,即3号节点。也就是说头指针走n步可以到达环的入口。
我们假设在环开头前的结点数量为x,环中的结点数量为y,那么有 n = x + y n=x+y n=x+y,即6=2+4。假设有两个指针 p 1 , p 2 p_1,p_2 p1,p2指向头节点, p 1 p_1 p1先走 y y y步,到达节点5,距离走到环的开头还需走 n − y = x n-y=x ny=x步,也就是再走2步。如果此时 p 2 p_2 p2从头节点出发,那么再走 x x x步即可和 p 1 p_1 p1相遇。因此,我们可以先让 p 1 p_1 p1先走 y y y步,然后 p 2 p_2 p2 p 1 p_1 p1同时移动,相遇时一定是环的入口节点,并且同时移动的距离为 x x x
这里之所以这么做而不是直接让 p 1 p_1 p1 p 2 p_2 p2直接向前走 x x x步,是因为代码实现时计算 x x x并不方便,因为想要计算 x x x,需要知道环的入口节点在哪,而我们的目标就是求环的入口节点,因此求 x x x没有意义。而计算 y y y更方便,因此实际上并不知道 x x x的大小,当然也不知道 n n n的大小。前面只是为了好分析将 n n n x x x实际大小写了出来,实际代码实现只有 y y y
此时我们的思路已经明确了,我们需要做三件事:(1)通过快慢指针来判断链表是否有环;(2)计算出环中的结点;(3)定义两个指针,其中一个指针先走“环中结点数量”步,然后第二个指针和第一个指针同时移动,相遇时即是环的入口位置。
在LeetCode提交代码如下所示:

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

class Solution:
    # 通过快慢指针判断链表是否有环
    def isCircle(self,head):
        if (head == None):
            return False
        # 定义快慢指针
        fast_pointer = head
        slow_pointer = head
        # 如果链表只有一个节点,说明没有环
        if(slow_pointer.next == None):
            return False
        
        while(fast_pointer != None):
            slow_pointer = slow_pointer.next
            fast_pointer = fast_pointer.next
            if(fast_pointer == None):
                return False
            fast_pointer = fast_pointer.next
            if(slow_pointer == fast_pointer):
                return slow_pointer
        return False

    # 判断环中节点的数量
    def circleNum(self,node):
        temp_node = node
        node = node.next
        count = 1
        while(node != temp_node):
            count += 1
            node = node.next
        return count
            
        


    def detectCycle(self, head: ListNode) -> ListNode:
        circle_node = self.isCircle(head)
        if(circle_node == False):
            return None
        else:
            circle_num = self.circleNum(circle_node)
            p_1 = head
            p_2 = head
            # p_1先走circle_num步
            for i in range(circle_num):
                p_1 = p_1.next
            while(p_1 != p_2):
                p_1 = p_1.next
                p_2 = p_2.next
            return p_1

运行结果如下所示:
运行结果

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值