重温 Leetcode 经典题之 142. 检测环形链表 II
2023-11-07 18:50:15
环形链表:判断和处理方法详解
什么是环形链表?
环形链表是一种链表,其中某个节点指向链表中的另一个节点,形成一个闭合的环路。在编程中,环形链表可能导致程序陷入死循环或其他异常行为,因此判断和处理它们至关重要。
两种环形链表判断算法
Floyd 判圈算法
原理:
Floyd 判圈算法使用两个指针,快指针和慢指针,以不同的速度遍历链表。如果链表中存在环,快指针最终将追上慢指针,相遇的节点就是环的入口节点。
步骤:
- 初始化快指针和慢指针,均指向链表头部。
- 快指针每次移动两步,慢指针每次移动一步。
- 重复步骤 2,直到快指针和慢指针相遇或快指针到达链表尾部。
- 如果快指针和慢指针相遇,则链表中存在环,相遇的节点就是环的入口节点。
代码示例:
def detect_cycle(head):
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
break
if slow != fast:
return None
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow
双指针法
原理:
双指针法也使用两个指针,指针 1 和指针 2,以相同的速度遍历链表。如果链表中存在环,指针 1 和指针 2 最终将相遇,相遇的节点就是环的入口节点。
步骤:
- 初始化指针 1 和指针 2,均指向链表头部。
- 指针 1 和指针 2 同时移动一步。
- 重复步骤 2,直到指针 1 和指针 2 相遇或指针 1 或指针 2 达到链表尾部。
- 如果指针 1 和指针 2 相遇,则链表中存在环,相遇的节点就是环的入口节点。
代码示例:
def detect_cycle(head):
ptr1 = head
ptr2 = head
while ptr1 and ptr2 and ptr2.next:
ptr1 = ptr1.next
ptr2 = ptr2.next.next
if ptr1 == ptr2:
break
if ptr1 != ptr2:
return None
ptr1 = head
while ptr1 != ptr2:
ptr1 = ptr1.next
ptr2 = ptr2.next
return ptr1
算法的时空复杂度分析
Floyd 判圈算法和双指针法的时空复杂度均为 O(n),其中 n 为链表的长度。这是因为这两种算法都需要遍历整个链表一次。
结论
环形链表的判断和处理是编程中的一项重要技能。Floyd 判圈算法和双指针法是两种经典的算法,它们简单高效,易于理解和实现。掌握这些算法,可以帮助你轻松解决环形链表相关的问题。
常见问题解答
1. 如何确定环的长度?
找到环的入口节点后,可以遍历环,并计算节点的数量,以获得环的长度。
2. 如何删除环?
要删除环,可以找到环的入口节点,然后将环中最后一个节点的 next 指针设置为 null。
3. 环形链表有什么实际应用?
环形链表可用于实现循环缓冲区、LRU 缓存和其他需要循环数据结构的情况。
4. Floyd 判圈算法和双指针法的区别是什么?
Floyd 判圈算法使用不同的移动速度,而双指针法使用相同的移动速度。Floyd 判圈算法可以更快地找到环,但双指针法更容易实现。
5. 如何判断一个链表是否是环形链表?
使用 Floyd 判圈算法或双指针法可以判断一个链表是否是环形链表。如果算法找到了环,则链表是环形链表,否则不是。