23. 合并K个排序链表

题目描述:

合并 k 个排序链表,返回合并后的排序链表。请分析和描述算法的复杂度。

示例:

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

优先级队列的概念:

参考:

  • https://www.cnblogs.com/xzxl/p/7266404.html
  • https://blog.csdn.net/pzhu_cg_csdn/article/details/79166858

优先级队列与普通队列一样,只能从队尾插入元素,从队首删除元素。但是它有一个特性,就是队列中最大的元素总是位于队首,所以出队时,并非按照先进先出的原则进行,而是将当前队列中最大的元素出队。优先级队列底层是用堆实现的。

Python的PriorityQueue是用 heapq 实现的。( https://blog.csdn.net/liu2012huan/article/details/53264162 )

代码:

from Queue import PriorityQueue
# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        q = PriorityQueue(maxsize=len(lists))
        cur = dummy = ListNode(None)
        for idx, node in enumerate(lists):
            if node:
                q.put((node.val, idx, node))
        while q.qsize() > 0:
            _, idx, cur.next = q.get() # get()会将节点弹出
            cur = cur.next
            if cur.next:
                q.put((cur.next.val, idx, cur.next))
        return dummy.next

这里有一些细节需要注意:

  • 优先级队列需要明确将哪个量设置为优先级的评价标准,这里 node.val 就是用来作为这一标准的;
  • 为什么还要引入 idx ?这是因为如果“第一评价标准”的优先级相同(这里即为 node.val ),代码就会比较“第二评价标准”。这里如果不设置“第二评价标准”,代码便会将 node 作为“第二评价标准”,而 node 是一个节点类,可能没有重写比较的方法,此时代码就会报错(这便是在 https://leetcode.com/problems/merge-k-sorted-lists/discuss/10511/10-line-python-solution-with-priority-queue 中 @ __fibo__ 提到的问题)。因此必须要找一个辅助变量作为“第二评价标准”,这里选取的是节点在列表中的索引。

复杂度分析:(强烈建议参考: https://blog.csdn.net/hellozhxy/article/details/81055397 )

前已述及,优先级队列的底层是用堆实现的。因此往一个长度为 的优先级队列中插入一个元素的时间复杂度为 。如果总共有 个元素,则将这 个元素插入这个长度为 的优先级队列中的时间复杂度为 。

现在代码中优先级队列的最大长度为 ,把最初的 个头结点插入这个优先级队列的时间复杂度为 。

关于时间复杂度的分析,@ gulshan98125 认为:假设 是所有链表中最长的链表长度,现在总共有 个链表,则总的时间复杂度为 ;而 @spitza 认为,把 当成是 个链表中所有的节点个数显得更直观一些,这样一来时间复杂度将会变成 。我个人的观点是,把 当成是这 个链表的平均长度,这样时间复杂度为 。

空间消耗在于维护一个长度为 的优先级队列(其实是维护一个容量为 的堆),因此空间复杂度为 。

你可能感兴趣的:(23. 合并K个排序链表)