返回

巧分链表:让小于 x 的元素优先排队

前端

前言

在计算机科学的海洋中,链表可谓是一艘不可或缺的航船。今天,我们踏上算法之旅,探索一个看似简单却蕴含妙趣的课题:如何将链表中小于特定值 x 的元素与大于或等于 x 的元素分割开来。leetcode 86 题正是这样一个分而治之的典型案例。

任务解读

给你一个链表的头节点 head 和一个特定值 x,你的使命是将链表一分为二:

  • 第一部分包含所有小于 x 的节点,按原有顺序排列。
  • 第二部分包含所有大于或等于 x 的节点,同样按原有顺序排列。

两个部分之间可以采用任何连接方式。

算法精髓:分而治之

分而治之是一种强大的算法范式,它通过递归地将大问题分解成更小的子问题来解决。对于链表分割问题,我们可以将链表递归地分成两部分:

  • 基线情况: 如果链表为空或只有一个节点,则不需要分割。
  • 递归步骤: 否则,比较头节点的值与 x:
    • 如果头节点值小于 x,则将其保留在第一部分,并递归处理剩余链表。
    • 如果头节点值大于或等于 x,则将其移至第二部分,并递归处理剩余链表。
  • 合并步骤: 将第一部分的尾节点与第二部分的头节点连接起来。

代码实现

def partition(head: ListNode, x: int) -> ListNode:
    """
    将链表分割为两部分,小于 x 的元素在前,大于或等于 x 的元素在后。

    参数:
    head:链表的头节点
    x:分割值

    返回:
    分割后的链表头节点
    """
    # 定义两个虚拟头节点,用于连接两个部分
    pre_head1 = ListNode(0)
    pre_head2 = ListNode(0)
    ptr1 = pre_head1
    ptr2 = pre_head2

    # 遍历原链表
    while head:
        # 如果当前节点的值小于 x
        if head.val < x:
            # 将其添加到第一部分
            ptr1.next = head
            ptr1 = ptr1.next
        # 否则
        else:
            # 将其添加到第二部分
            ptr2.next = head
            ptr2 = ptr2.next
        # 移动到下一个节点
        head = head.next

    # 连接两个部分
    ptr1.next = pre_head2.next
    ptr2.next = None

    # 返回第一部分的头节点
    return pre_head1.next

假设我们有一个链表:[1, 4, 3, 2, 5, 2],分割值为 x = 3。

  • 比较头节点 1 与 3:1 < 3,将其保留在第一部分。
  • 递归处理剩余链表 [4, 3, 2, 5, 2]。
  • 比较头节点 4 与 3:4 > 3,将其移至第二部分。
  • 递归处理剩余链表 [3, 2, 5, 2]。
  • 比较头节点 3 与 3:3 = 3,将其保留在第一部分。
  • 递归处理剩余链表 [2, 5, 2]。
  • 继续此过程,直到所有节点都被处理完毕。
  • 最终,我们将第一部分和第二部分连接起来:
第一部分:[1, 3]
第二部分:[4, 2, 5, 2]

通过巧妙地运用分而治之思想,我们轻而易举地解决了链表分割问题。这个算法不仅高效实用,而且为我们提供了一种解决复杂问题的思路。下次遇到链表难题时,不妨试试分而治之,也许能带给你意想不到的惊喜!