题目描述:
合并 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 认为,把 当成是 个链表中所有的节点个数显得更直观一些,这样一来时间复杂度将会变成 。我个人的观点是,把 当成是这 个链表的平均长度,这样时间复杂度为 。
空间消耗在于维护一个长度为 的优先级队列(其实是维护一个容量为 的堆),因此空间复杂度为 。