【问题标题】:How to merge k number of sorted lists?如何合并k个排序列表?
【发布时间】:2019-03-20 03:26:30
【问题描述】:
def merge(list1, list2):
    results = []
    while list1 and list2:
        if list1[0] < list2[0]:
            results.append(list1.pop(0))
        else:
            results.append(list2.pop(0))
    results.extend(list1)
    results.extend(list2)
    return results

这是将 2 个已排序列表合并为 1 个的标准算法。但是,我们如何将多个已排序列表合并为 1 个?

l = [[8, 10, 12], [4, 5, 9], [2, 11]]  
merge(l)  
>>> [2, 4, 5, 8, 9, 10, 11, 12]

【问题讨论】:

标签: python arrays list sorting merge


【解决方案1】:

您可以使用自己的mergereduce

from functools import reduce

l = [[8, 10, 12], [4, 5, 9], [2, 11]]

merged = reduce(merge, l)
print(merged)
# [2, 4, 5, 8, 9, 10, 11, 12]

这有运行时间O(kn)。您可以合并(唯一)对,直到剩下 1 个最终列表,这会将其改进为 O(n log k)(因为每次要合并的列表数量减少一半)。

【讨论】:

  • 当 k 接近 sqrt(n) 时排序更快
  • @ChristianSloper O(n log k) 我们总是至少和 O(n log n) 一样好或更好(如k )。但是,是的,带有简单reduceO(kn) 对于大k 来说并不是那么好。重新排序的问题在于您没有利用子列表已经排序的事实。
  • 同意,我的评论是对代码的回应,我没有看到您的评论改进到 n log k。
【解决方案2】:

您可以使用heapqueues 实现direct k-way merge

import heapq
from collections import deque


def k_merge(*lists):
    queues = [queue for queue in map(deque, lists)]

    heap = []
    for i, lst in enumerate(queues):
        heap.append((lst.popleft(), i))

    heapq.heapify(heap)

    result = []
    while heap:
        value, index = heapq.heappop(heap)
        result.append(value)

        if queues[index]:
            heapq.heappush(heap, (queues[index].popleft(), index))

    return result


print(k_merge(*[[8, 10, 12], [4, 5, 9], [2, 11]]))

输出

[2, 4, 5, 8, 9, 10, 11, 12]

如果您有k 列表和n 元素,则此方法是O(nlogk)

【讨论】:

  • @OlivierMelançon 更新了答案!
【解决方案3】:

您可以使用sorted() 对其进行排序:

from itertools import chain

l = [[8, 10, 12], [4, 5, 9], [2, 11]]

sorted(chain(*l))

给出结果:

[2, 4, 5, 8, 9, 10, 11, 12]

【讨论】:

  • 虽然,这是 O(nlog(n))
【解决方案4】:

用列表推导展平,然后排序

print(sorted([j for i in l for j in i]))
# [2, 4, 5, 8, 9, 10, 11, 12]

【讨论】:

    【解决方案5】:
    from Queue import PriorityQueue
    
    class Solution(object):
        def mergeKLists(self, lists):
            """
            :type lists: List[ListNode]
            :rtype: ListNode
            """
            head = point = ListNode(0)
            q = PriorityQueue()
            for l in lists:
                if l:
                    q.put((l.val, l))
            while not q.empty():
                val, node = q.get()
                point.next = ListNode(val)
                point = point.next
                node = node.next
                if node:
                    q.put((node.val, node))
            return head.next
    

    使用优先队列优化比较过程

    1. 时间复杂度:O(n log(k)) 其中 k 是链表的数量:

      • 每次弹出和插入优先级队列的比较成本将降低到 O(log k)。但是找到具有最小值的节点只需要 O(1) 时间。
    2. 空间复杂度:

      • O(n) 创建一个新的链表需要 O(n) 空间
      • O(k) 上面的代码应用了原地方法,需要花费 O(1) 空间。
      • 优先级队列(通常用堆实现)需要 O(k) 空间(在大多数情况下远小于 N)

    【讨论】:

      【解决方案6】:

      一个简单有效的方法是heapq.merge:

      >>> lists = [[8, 10, 12], [4, 5, 9], [2, 11]]
      >>> list(heapq.merge(*lists))
      [2, 4, 5, 8, 9, 10, 11, 12]
      

      看一眼CPython's implementation,它看起来类似于direct k-way merge answer,所以我heapq.merge 也是O(n log(k)),尽管文档字符串不能保证这一点。 p>

      【讨论】:

        猜你喜欢
        • 2018-05-16
        • 1970-01-01
        • 1970-01-01
        • 2011-02-11
        • 2018-04-19
        • 2013-10-28
        • 2021-07-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多