返回
LeetCode 86:分隔链表,空间复杂度优化背后的巧思
闲谈
2024-01-16 23:50:07
在软件开发领域,"分治法"是一种经典的解决复杂问题的策略。它将一个问题分解为若干个更小的子问题,分别解决这些子问题,然后将子问题的解组合起来得到原问题的解。这种方法在解决复杂算法问题时非常有用,而 LeetCode 86: 分隔链表 这道题就是一个很好的例子。
LeetCode 86: 分隔链表
给你一个头结点为 head 的单链表和一个整数 x ,返回一个链表,其中每 x 个节点组成一个块,并使用指针互相连接。
如果不足 x 个节点,最后剩余的节点应该保持原有顺序。
示例 1:
输入:head = [1,2,3,4,5,6,7,8,9,10], x = 3
输出:[1,2,3,6,7,8,10]
解释:
链表已分隔成 [(1,2,3), (4,5,6), (7,8,9), (10)]
示例 2:
输入:head = [1,2,3,4,5], x = 2
输出:[1,2,3,5]
解释:
链表已分隔成 [(1,2), (3,4,5)]
示例 3:
输入:head = [], x = 2
输出:[]
提示:
链表中节点的数目范围是 [0, 100]
0 <= Node.val <= 100
0 <= x <= 100
巧妙的解决方案
我们首先介绍一种非常巧妙的解决方案。该方案的思路是:
- 使用两个指针 slow 和 fast 指向链表的头部。
- 将 fast 指针向前移动 x 个节点。
- 如果 fast 指针指向最后一个节点或下一个节点是 null,那么将 slow 指针指向 fast 指针,并结束循环。
- 将 slow 指针指向 slow 指针的下一个节点。
- 重复步骤 2 和步骤 3,直到 fast 指针指向最后一个节点或下一个节点是 null。
- 将 slow 指针指向 null。
- 返回 head 指针。
def partition(head: ListNode, x: int) -> ListNode:
slow = head
fast = head
while fast and fast.next:
for _ in range(x-1):
fast = fast.next
if not fast:
break
if not fast or not fast.next:
break
slow.val, fast.val = fast.val, slow.val
slow = slow.next
fast = fast.next
return head
该方案的时间复杂度是 O(n),空间复杂度是 O(1)。
延伸
我们还可以使用其他分治方法来解决这道题。一种常见的方法是使用递归。我们可以将链表分成两部分,然后递归地对这两部分进行分隔。最后,我们将这两部分连接起来,就得到了最终的结果。
def partition(head: ListNode, x: int) -> ListNode:
if not head:
return head
left_head = ListNode(0)
right_head = ListNode(0)
left = left_head
right = right_head
while head:
if head.val < x:
left.next = head
left = left.next
else:
right.next = head
right = right.next
head = head.next
left.next = partition(right_head.next, x)
return left_head.next
这种递归方法的时间复杂度也是 O(n),但空间复杂度是 O(n),因为我们需要额外创建一个新的链表。
无论使用哪种方法,我们都可以解决这道题。分治法是一种非常强大的解决复杂问题的策略,在解决各种各样的算法问题时都非常有用。