合并K个排序链表

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

示例:

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

方法 1:暴力

想法 & 算法

    遍历所有链表,将所有节点的值放到一个数组中。
    将这个数组排序,然后遍历所有元素得到正确顺序的值。
    用遍历得到的值,创建一个新的有序链表。

#python
class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        self.nodes = []
        head = point = ListNode(0)
        for l in lists:
            while l:
                self.nodes.append(l.val)
                l = l.next
        for x in sorted(self.nodes):
            point.next = ListNode(x)
            point = point.next
        return head.next

复杂度分析

    时间复杂度:O(Nlog⁡N) ,其中 N 是节点的总数目。
        遍历所有的值需花费 O(N)的时间。
        一个稳定的排序算法花费 O(Nlog⁡N) 的时间。
        遍历同时创建新的有序链表花费 O(N) 的时间。

    空间复杂度:O(N)。
        排序花费 O(N) 空间(这取决于你选择的算法)。
        创建一个新的链表花费 O(N)的空间。

方法 2:逐一比较

算法

    比较 k个节点(每个链表的首节点),获得最小值的节点。
    将选中的节点接在最终有序链表的后面。

复杂度分析

    时间复杂度: O(kN),其中 k是链表的数目。
        几乎最终有序链表中每个节点的时间开销都为 O(k) (k-1次比较)。
        总共有 N个节点在最后的链表中。

    空间复杂度:
        O(n)。创建一个新的链表空间开销为 O(n)。
        O(1)。重复利用原来的链表节点,每次选择节点时将它直接接在最后返回的链表后面,而不是创建一个新的节点。

方法 3:用优先队列优化方法 2

算法

几乎与上述方法一样,除了将 比较环节优先队列 进行了优化。

#python
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

复杂度分析

    时间复杂度: O(Nlog⁡k) ,其中 k 是链表的数目。
        弹出操作时,比较操作的代价会被优化到 O(log⁡k)。同时,找到最小值节点的时间开销仅仅为 O(1)。
        最后的链表中总共有 N 个节点。

    空间复杂度:
        O(n) 。创造一个新的链表需要 O(n)的开销。
        O(k) 。以上代码采用了重复利用原有节点,所以只要 O(1) 的空间。同时优先队列(通常用堆实现)需要 O(k)的空间(远比大多数情况的 N要小)。

方法 4:逐一两两合并链表

算法

将合并 k个链表的问题转化成合并 2 个链表 k-1次。

复杂度分析

    时间复杂度: O(kN),其中 k是链表的数目。
        我们可以在 O(n) 的时间内合并两个有序链表,其中 n是两个链表的总长度。
        把所有合并过程所需的时间加起来,我们可以得到:O(kN)。

    空间复杂度:O(1)
        我们可以在 O(1)空间内合并两个有序链表。

 

方法 5:分治

想法 & 算法

这个方法沿用了上面的解法,但是进行了较大的优化。我们不需要对大部分节点重复遍历多次。

    将 k个链表配对并将同一对中的链表合并。
    第一轮合并以后, k个链表被合并成了 k/2个链表,平均长度为 2N/k ,然后是 k/4 个链表, k/8个链表等等。
    重复这一过程,直到我们得到了最终的有序链表。

因此,我们在每一次配对合并的过程中都会遍历几乎全部 N个节点,并重复这一过程\log_2K  次。

                                 

#Python
class Solution(object):
    def mergeKLists(self, lists):
        """
        :type lists: List[ListNode]
        :rtype: ListNode
        """
        amount = len(lists)
        interval = 1
        while interval < amount:
            for i in range(0, amount - interval, interval * 2):
                lists[i] = self.merge2Lists(lists[i], lists[i + interval])
            interval *= 2
        return lists[0] if amount > 0 else lists

    def merge2Lists(self, l1, l2):
        head = point = ListNode(0)
        while l1 and l2:
            if l1.val <= l2.val:
                point.next = l1
                l1 = l1.next
            else:
                point.next = l2
                l2 = l1
                l1 = point.next.next
            point = point.next
        if not l1:
            point.next=l2
        else:
            point.next=l1
        return head.next

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值