23.合并K个排序链表

题目

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

示例:

输入:

[

  1->4->5,

  1->3->4,

  2->6

]

输出: 1->1->2->3->4->4->5->6

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


题解

此学习笔记,参考题解:

感谢!!

https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/duo-tu-yan-shi-23-he-bing-kge-pai-xu-lian-biao-by-/

https://leetcode-cn.com/problems/merge-k-sorted-lists/solution/leetcode-23-he-bing-kge-pai-xu-lian-biao-by-powcai/

优先队列

依靠最小堆的特性,每次弹出必定是最小值

堆排序

步骤:

  1. 将所有节点的加入到堆队列中
  2. 不断弹出,生成新的链表

说明: 在python中,我们默认创建的实例之间不能使用 比较运算符 进行比较,是因为没有重写比较方法__lt__ , __gt__, __le__, __ge__, __eq__

所以可以重写__lt__方法,使得〔ListNode实例〕之间可以大小比较

def __lt__(self, other):
    return self.val < other.val
ListNode.__lt__ = __lt__

完整代码:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists: return None
        import heapq
        queue = []
        dummy = ListNode(-1)
        def __lt__(self, other):
            return self.val < other.val
        ListNode.__lt__ = __lt__
        cur = dummy
        # 将所有节点加入至堆队列中
        for l in lists:
            while l:
                heapq.heappush(queue, l)
                l = l.next
        # 不断取出节点
        while queue:
            # 由于最小堆的特性,每次取出的节点都是最小的
            node = heapq.heappop(queue)
            cur.next = node
            cur = cur.next
        cur.next = None
        return dummy.next

空间复杂度: O(N)

堆排序优化

此方法是对上一方法的优化,在上面方法中是直接将所有的节点一股脑全部丢到堆队列中,这造成了空间的浪费。在此,可以将空间复杂度优化成O(K),即链表的数量

将每个链表的头结点,加入到堆队列即可,当此节点出队列时,立即判断此节点的指针域是否为空,不为空就表示后面还有节点,所以将后面的节点加入到堆队列中。如此一来,堆队列的空间大小就没有发生变换。

步骤:

  1. 将所有链表的头节点加入到堆队列中
  2. 出队,将出来的节点连接到新节点上dummy
  3. 检测刚出队的节点的指针域是否有效,有效则将下一个节点加入到堆队列中

完整代码:

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

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        
        def __lt__(self, other):
            return self.val < other.val
        ListNode.__lt__ = __lt__
        
        if not lists:
            return
        import heapq
        queue = []
        dummy = ListNode(-1)
        cur  = dummy
        # 将所有的链表的头结点入队
        for i in range(len(lists)):
            head = lists[i]
            if head:
                heapq.heappush(queue, head)

        # 从堆队列中弹出并检测是否还有下一个节点,有则加入到堆队列中
        while queue:
            node = heapq.heappop(queue)
            cur.next = node
            cur = cur.next
            # 下一个节点是否有效
            if node.next:
                heapq.heappush(queue, node.next)
        cur.next = None
        return dummy.next

两两合并

[简单]合并两个有序链表:https://leetcode-cn.com/problems/merge-two-sorted-lists/

之前做过一道题,实现两个有序链表的合并

本题是多个有序链表的合并,所以自然可以使用使用两两合并,合并到最后即成为一个有序链表

完整代码:

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

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        dummy = ListNode(float('-inf'))
        cur = dummy
        for i in range(len(lists)):
            cur = self.mergeTwoLists(cur, lists[i])
        return dummy.next

    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        dummy = ListNode(-1)
        cur = dummy
        while l1 and l2:
            if l1.val < l2.val:
                cur.next = l1
                l1 = l1.next
            else:
                cur.next = l2
                l2 = l2.next
            cur = cur.next
        cur.next = l1 if l1 else l2
        return dummy.next
            

然而,效率极低

分治法

在排序算法中,一个〔归并排序〕

将无序的序列分解成一个一个,然后按有序方式组合

归并排序中的 分治思想 可以在此题应用到

按照归并排序的案例或上图的演示,我们首先将 lists 中的链表一分再分, 然后按照大小关系一一合并

步骤:

  1. lists 一分再分,分到不能再分为止
  2. 不断合并
  3. 最后得到完整的有序链表

步骤描述只是大概,具体还是得根据代码来慢慢理解,所以一定要写注释!!!

完整代码:

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

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        if not lists: return 
        return self.merge(0, len(lists)-1, lists)
    
    def merge(self, left, right, lists):
        """将lists不断分割,这里的操作有点像二分法
        通过不断的一分二,分到最小单位
        """
        if left == right:
            return lists[left]
        mid = (left + right) >> 1
        l1 = self.merge(left, mid, lists)
        l2 = self.merge(mid+1, right, lists)
        # 调用合并函数,将 l1 和 l2合并成新有序链表
        return self.mergeTwoLists(l1, l2)

    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        """将两个链表合成一个有序链表"""
        dummy = ListNode(-1)
        cur = dummy
        while l1 and l2:
            if l1.val < l2.val:
                cur.next = l1
                l1 = l1.next
            else:
                cur.next = l2
                l2 = l2.next
            cur = cur.next
        cur.next = l1 if l1 else l2
        return dummy.next