刷题总结:环形链表

62 阅读4分钟

链表的结构看似简单,实则隐藏着诸多复杂的问题等待我们去挖掘。其中,环形链表的判定与处理便是一道颇具难度的关卡。如何准确判断一个链表是否存在环?若存在环,又该如何找到环的入口节点?

首先介绍一个概念,快慢指针: 什么是快慢指针呢?顾名思义就是一个指针走的快一点一个指针走的慢一点。

请看例题:

给你一个链表,让你返回一个中间节点。 leetcode.cn/problems/mi…

解决方法这个时候,你定义一个快指针指向头节点,定义一个慢指针指向头节点,快指针一次走两个,慢指针一次走一个。当快指针遍历完成之后,慢指针是不是刚好遍历一半,所以慢指针就是答案。但是,在链表长度奇偶性不一样时,又要分类讨论。链表长度为奇数时,假设链表为123,中间节点为2,慢指针指向2时快指针指向3(最后一个节点),当链表长度为奇数时,假设为1234,中间节点为3,当慢指针指向3时,快指针指向4的下一个节点为空。所以循环条件就是当快指针不为空且快指针的下一个节点不为空while fast and fast.next 代码实现

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]:
        slow=head
        fast=head
        while fast and fast.next:
            slow=slow.next
            fast=fast.next.next
        return slow

但是快慢指针的运用还远不止这些

请看下一题:

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

leetcode.cn/problems/li…

分析:这道题同样用快慢指针做,假设没环,慢指针是不可能追上快指针的,假设有环,就像操场一样,快指针跑完一圈了,会与慢指针相遇(俗称套圈)这样,假如在循环过程中,快指针与慢指针相遇了,那么就说明有环。

代码实现

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

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        fast=head
        slow=head
        while fast and fast.next:
            fast=fast.next.next
            slow=slow.next
            if fast is slow:
                return True
        return

仅此而已吗? nonono

如果再让你找到环的入口呢?

下一道例题:

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

leetcode.cn/problems/li…

分析 如图

image.png 设头节点到入口的距离为a

入口到相遇点的距离为b

相遇点到入口的距离为c

环长为b+c

慢指针距离=a+b+K1(b+c)

快指针距离=a+b+K2(b+c)

慢的距离的两倍等于快的距离

2[a+b+K1(b+c)]=a+b+K2(b+c)

化简得 a-c=(k2-1-2k1)(b+c)

这个等式意味着慢指针在相遇后继续走到入口,一个指针(head)在头节点也同时往后走,最后它俩相遇一定在入口

代码实现

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

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        fast=head
        slow=head
        while fast and fast.next:
            fast=fast.next.next
            slow=slow.next
            if fast is slow:
                while head is not slow:
                    head=head.next
                    slow=slow.next
                return slow
        return None

好了,具体的应用还是得到实际题目中(完结)