23. 合并K个升序链表

220 阅读4分钟

给你一个链表数组,每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

解法一

自己看答案前的做法,主要思想:和合并两个有序链表一样,每次比较k个链表的节点值大小,并记录下来,将比较的那个链表指向next。

代码如下,附详细注释

def mergeKLists1(lists):

    # 特殊情况判断
    if not lists: return None

    dummy = ListNode()  # 哑节点
    pre = dummy

    isGoon = True  # 标记是否还需要进行一次大的循环
    while isGoon:
        isGoon = False  # 默认每次进来都为False
        isFirst = True  # 记录是否是第一个节点
        index = -1
        for i in range(len(lists)):  # 遍历整个链表数组
            ln = lists[i]
            if ln is not None:
                isGoon = True  # 如果有节点不为None, 那么就设置为True,继续进行大循环
                if isFirst:  # 链表不为None时,记录第一个节点和该链表位于数组中的位置
                    temp = ln
                    index = i
                    isFirst = False
                else:
                    if ln.val < temp.val:  # 如果后面有节点比前面节点的值小,就记录下来
                        temp = ln
                        index = i
        if index >= 0:  # 如果记录的有节点
            pre.next = temp  # 就将上面比较下来的最小节点作为pre的下一个节点
            pre = pre.next  # 并将pre指针指向下一个节点
            lists[index] = lists[index].next  # 将刚刚使用的数组中的链表指向它的下一个节点,用于后面的比较
            # 如果数组中有个链表为None了,就移除数组,减少后面的遍历
            if lists[index] is None:
                del lists[index]

    return dummy.next  # 返回哑节点的下面节点,就是所求结果

解法二

队列 参考力扣官方答案

主要思想:维护当前每个链表没有被合并的元素的最前面一个,k 个链表就最多有 k 个满足这样条件的元素,每次在这些元素里面选取 val 属性最小的元素合并到答案中。在选取最小元素的时候,我们可以用优先队列来优化这个过程。

image.png Python2的写法:

from Queue import PriorityQueue
class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        head = point = ListNode()  # 声明头结点和构建链表的节点
        q = PriorityQueue()  # 声明一个优先级队列
        for l in lists:
            if l:
                q.put((l.val, l))  # 将 第一个节点和队列作为一个元祖,也就是item放入优先队列当中
        # 循环迭代队列,直到队列为空
        while not q.empty():
            val, note = q.get()  # 这里每次取出的节点和队列,就是val值最小的节点对应的那个item
            point.next = ListNode(val)  # 将节点值放入最终有序链表中
            point = point.next  # 将指针指向下一个节点
            note = note.next  # 将取出的这个链表的指针也指向下一个节点
            if note:
                q.put((note.val, note))  # 如果节点不为空,再继续放入队列中
        return head.next

Python3的写法:

# 导入优先级队列
from queue import PriorityQueue

def mergeKLists2(lists):
    """使用队列求解"""
    head = point = ListNode()  # 声明头结点和构建链表的节点
    q = PriorityQueue()  # 声明一个优先级队列
    for index, l in enumerate(lists):
        if l:
            # python 优先队列 PriorityQueue,python3 不允许插入优先级相同的值或者是无法判断优先级的值到优先队列。
            q.put((l.val, index, l))  # 将 第一个节点和队列作为一个元祖,也就是item放入优先队列当中
    # 循环迭代队列,直到队列为空
    while not q.empty():
        val, index, note = q.get()  # 这里每次取出的节点和队列,就是val值最小的节点对应的那个item
        point.next = ListNode(val)  # 将节点值放入最终有序链表中
        point = point.next  # 将指针指向下一个节点
        note = note.next  # 将取出的这个链表的指针也指向下一个节点
        if note:
            q.put((note.val, index, note))  # 如果节点不为空,再继续放入队列中
    return head.next

解法三

分治 参考力扣官方答案

主要思想:先将数组中的链表两两合并,然后再将合并的链表,继续两两合并,最后剩下的那个链表就是所求的链表。这里使用求两个有序链表合并的方法。

def mergeKLists3(lists):
    """分治"""
    def merge2Lists(l1, l2):
        """合并两个有序链表"""
        head = point = ListNode()
        while l1 and l2:
            if l1.val <= l2.val:
                point.next = l1
                l1 = l1.next
            else:
                point.next = l2
                l2 = l2.next
            point = point.next
        point.next = l1 if l1 is not None else l2
        return head.next

    amount = len(lists)  # 记录输入列表的个数
    interval = 1  # 记录初始间隔为 1
    while interval < amount:  # 通过while循环将输入队列两两合并
        for i in range(0, amount - interval, interval * 2):
            lists[i] = merge2Lists(lists[i], lists[i + interval])  # 合并两个链表
        interval *= 2  # 间隔数乘以2
    return lists[0] if amount > 0 else None  # 最后剩下的第一个链表,就是所求链表