返回

巧用优先队列征服LeetCode 23:合并K个升序链表

前端

亲爱的小伙伴们,今天,我们踏上了一段探索如何合并多个升序链表的征程,而我们的秘密武器就是优先队列,准备好了吗?让我们开始吧!

优先队列:小顶堆的奇妙世界

小顶堆是一种数据结构,它可以高效地存储和管理一系列元素,并始终将最小值保存在堆顶。在我们的任务中,我们将使用小顶堆来存储每个链表的头节点。

每次,我们从堆顶弹出最小值,然后将其余节点加入到堆中。这样,我们就能一步一步地合并链表,直到所有元素都被合并到一个升序链表中。

算法流程:一步一步合并

  1. 初始化优先队列: 将每个链表的头节点加入到优先队列中。
  2. 循环合并:
    • 从堆顶弹出最小值的链表头节点,加入到合并后的链表中。
    • 将该链表的头节点指向下一个节点,如果存在的话。
    • 如果该链表还有剩余节点,将其头节点加入到优先队列中。
  3. 重复步骤 2, 直到优先队列为空,即所有链表都已被合并。

代码示例:清晰易懂的实现

import heapq

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        # 初始化优先队列
        heap = []
        for node in lists:
            if node:
                heapq.heappush(heap, (node.val, node))

        # 循环合并
        dummy = ListNode()
        current = dummy
        while heap:
            # 从堆顶弹出最小值
            val, node = heapq.heappop(heap)

            # 合并到最终链表中
            current.next = node
            current = current.next

            # 将剩余节点加入到堆中
            if node.next:
                heapq.heappush(heap, (node.next.val, node.next))

        return dummy.next

示例:一步步理解合并过程

假设我们有三个升序链表:

  • L1:1 -> 4 -> 5
  • L2:1 -> 3 -> 4
  • L3:2 -> 6

初始化:

优先队列:[(1, L1), (1, L2), (2, L3)]

步骤 1:

从堆顶弹出最小值:(1, L1)
合并后的链表:1

L1:4 -> 5
优先队列:[(1, L2), (2, L3)]

步骤 2:

从堆顶弹出最小值:(1, L2)
合并后的链表:1 -> 1

L2:3 -> 4
优先队列:[(2, L3)]

步骤 3:

从堆顶弹出最小值:(2, L3)
合并后的链表:1 -> 1 -> 2

L3:6
优先队列:[(4, L1), (3, L2), (6, L3)]

步骤 4:

从堆顶弹出最小值:(3, L2)
合并后的链表:1 -> 1 -> 2 -> 3

L2:4
优先队列:[(4, L1), (6, L3)]

步骤 5:

从堆顶弹出最小值:(4, L1)
合并后的链表:1 -> 1 -> 2 -> 3 -> 4

L1:5
优先队列:[(6, L3)]

步骤 6:

从堆顶弹出最小值:(6, L3)
合并后的链表:1 -> 1 -> 2 -> 3 -> 4 -> 6

最终合并后的链表为:1 -> 1 -> 2 -> 3 -> 4 -> 6

结语

至此,我们已经掌握了使用优先队列合并多个升序链表的技术,这让我们在解决链表相关问题时有了强大的武器。希望这篇文章对你有帮助,让我们继续探索算法世界的奇妙之处吧!