打败算法 —— 环形链表 II
本文参考
出自LeetCode上的题库 —— 环形链表II,哈希表和快慢指针两种解法都需要O(n)的时间,但快慢指针仅占用O(1)的空间
https://leetcode-cn.com/problems/linked-list-cycle-ii/
环形链表问题
给定一个链表的头节点 head,返回链表开始入环的第一个节点(不允许修改链表)
如果链表无环,则返回null。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环
示例1:
输入:head = [3,2,0,-4]
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点
示例2:
输入:head = [1,2]
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点
示例3:
输入:head = [1]
输出:返回 null
解释:链表中没有环
解题思路
首先是特殊情况,当没有节点,或有节点却不存在环的情况下,容易通过节点或节点的next指针是否空进行判断
当节点间存在环路时,第一种直观的解法,使用一个指针沿着链路走下去,每走一步将节点存入set集合,当碰到重复出现的节点时,该节点就是环路的入口;
第二种解法需要应用两遍双指针,可能刚接触这道题时已经想到了快慢指针,但是我们无法保证快指针和慢指针相遇的位置一定是环路的入口,以至于否定了这种解法。此处需要一定的数学分析,在快慢指针相遇的位置引入第三个指针:
令总节点数为,头节点为,环路的入口节点为,环路中快慢指针相遇的节点位置为,定义距离,距离,距离,满足
当快慢指针相遇时,快指针走过的路径长度为,慢指针走过的路径长度为,我们预先设置快指针每次走两步,慢指针每次走一步,则,化简得,的长度是的长度加上圈的环路长度
因此,我们引入第三个指针ptr,慢指针从相遇位置开始移动,ptr从头指针开始移动,由公式可知,二者相遇的位置即为环路的路口
哈希表解法
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def detect_cycle_1(self, head: ListNode) -> ListNode:
if head is None or head.next is None:
return None
node_set = set()
curr_node = head
while curr_node not in node_set:
node_set.add(curr_node)
if curr_node.next is None:
return None
else:
curr_node = curr_node.next
return curr_node
快慢指针解法
def detect_cycle_2(self, head: ListNode) -> ListNode:
slow = fast = head
while fast is not None:
# 慢指针每次走一步
slow = slow.next
if fast.next is None:
return None
# 快指针每次走两步
fast = fast.next.next
# 快慢指针相遇
if fast == slow:
ptr = head
# 建立新的双指针走法
while ptr != slow:
ptr = ptr.next
slow = slow.next
return ptr
return None
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)