每日leetcode——142. 环形链表 II
题目
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
遍历+哈希 最简单的办法就是遍历每个节点,同时把节点存到一个字典中,当遍历下一个节点时,看看该节点是否已经存在于字典中,如果是,就说明该节点是环入口节点
def detectCycle(head: ListNode) -> ListNode:
hashTable = {}
while head:
if head in hashTable:
return head
hashTable[head] = 1
head = head.next
return None
时间复杂度:O(n)
空间复杂度:O(n)
双指针 理解双指针的思路,需要通过画图,计算移动距离的等量关系来理解:
文章图片
在链表头节点,定义两个指针:fast和slow
fast每次移动2个节点,slow每次移动1个节点
如果链表没有环,fast指针会先移动到null处
如果链表有环,fast先进入环,slow随后进入环,最终他们肯定会在环中的某个节点相遇
假设fast和slow第一次相遇时的节点如图所示
a代表链表头节点到环的入口节点的节点数
b代表环的入口节点到fast和slow第一次相遇的节点的节点数
c代表第一次相遇节点到环的入口节点的节点数
此时,fast走过的节点数为 a+b+n(c+b) ,n表示fast在环中绕的圈数
slow走过的节点数为 a+b
fast和slow是一同移动的,所以任意时刻他们的移动步数是相同的,但是fast每次移动2个节点,相同的步数下,fast移动的节点数应该是slow的2倍
所以 a+b+n(c+b) = 2(a+b)
整理等式得:a=n(b+c)-b = nb+nc-b = (n-1)b+nc = (n-1)b+(n-1)c+c=(n-1)(b+c)+c
得 a = (n-1)(b+c)+c
也就是说:头节点到环入口节点的距离 = 第一次相遇节点到环入口节点的距离+(n-1)圈环的长度
如果此时有两个指针a和b,a从头节点开始移动,b从slow和fast第一次相遇节点开始移动,均每次移动一个节点,a和b最终会在环入口节点处相遇。
此时,slow已经处在第一次相遇节点位置,因此当slow和fast第一次相遇时,再在头节点处定义一个指针,和slow一起每次移动1个节点,他们相遇的那个节点就是环入口节点。
def detectCycle(self, head: ListNode) -> ListNode:
# 排除空链表情况
if not head:
return None
fast,slow,ptr = head,head,headwhile fast:
# 排除链表只有一个节点的情况
if not fast.next:
return None
# 移动fast和slow
slow = slow.next
fast = fast.next.next
# fast和slow相遇时
if slow == fast:
# ptr和slow开始移动,直到相遇
while ptr != slow:
slow = slow.next
ptr = ptr.next
# 相遇时,返回该节点,就是入口节点
return ptr
return None# 跟简洁优雅版
def detectCycle(self, head: ListNode) -> ListNode:
fast, slow = head, head
while True:
if not (fast and fast.next): return
fast, slow = fast.next.next, slow.next
if fast == slow: break
fast = head
while fast != slow:
fast, slow = fast.next, slow.next
return fast
时间复杂度:O(N),其中 N 为链表中节点的数目。在最初判断快慢指针是否相遇时,slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)=O(N)。
【每日leetcode——142. 环形链表 II】空间复杂度:O(1)。我们只使用了slow,fast,ptr 三个指针。
推荐阅读
- 微信小程序|微信小程序项目实例——备忘录
- Android性能优化系列——网络和电量优化
- 微信小程序|微信小程序项目实例——飞机大战
- 爱啥啥啥Hadoop——(一)初次见面
- C/C++|有趣的C语言指针(二)——指针声明的那些事儿
- 蓝桥杯|蓝桥杯备战 每日训练3道 真题解析
- 力扣每日一题|力扣(每日一题)—— 2016. 增量元素之间的最大差值
- 力扣每日一题|力扣(每日一题)—— 1706. 球会落何处
- C++实现LeetCode(19.移除链表倒数第N个节点)
- 二叉树(Binary|LeetCode 536. Construct Binary Tree from String - 二叉树系列题18