LeetCode 23. 合并K个升序链表小根堆解法

You are given an array of k linked-lists lists, each linked-list is sorted in ascending order.

Merge all the linked-lists into one sorted linked-list and return it.

Example 1:

Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]
Explanation: The linked-lists are:
[
  1->4->5,
  1->3->4,
  2->6
]
merging them into one sorted list:
1->1->2->3->4->4->5->6

Example 2:

Input: lists = []
Output: []

Example 3:

Input: lists = [[]]
Output: []

Constraints:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] is sorted in ascending order.
  • The sum of lists[i].length won't exceed 10^4

从题目中可以看出k是每个列表的长度,然后设n为列表的个数。

暴力的解法就是从头开始,将前两个列表lists[0], lists[1]合并为一个新列表ANS,然后每次都拿ANS与之后的一个列表合并,直到将所有的lists[i]合并完成。

因为第一次归并要遍历k个元素,第二次是2k,然后3k, 4k 直到n-1k。这样的复杂度是O(n^2k),所以当n很大时容易超时(我试了一下,里面有个丧心病狂的k为1,然后n为10000的样例,妥妥的超时)。

所以为了避免多次重复的访问,我们需要同时合并所有的列表lists[i]。那么这样面对的问题就是怎么快速的比较n个list中哪个的首元素值最小。每次都遍历一次的话。因为每放入一个元素需要n次比较,而共有nk个元素。时间复杂度会还是O(n^2k)。所以我们要减少比较的复杂度。

很明显能实现这一要求的数据结构就是小根堆了。对于n个元素来说,小根堆能一直实现复杂度为log(n)的插入,和O(1)的取出最小值。综合来讲时间复杂度应该为O(nklog(n))。long(n)的原因是因为我们堆里只放每个list中的头元素,所以最多会有n个元素。

具体的解题思路是:

一开始将n的列表的第一个元素放入小根堆,然后从其中取出最小的那一个节点E,将E放到答案列表的最后面,若E的next不为空,则将E的next放入到小根堆里。直到堆为空。

然而奇怪的是,当我用另一种解法,就是先将所有lists里的值都取出来,放入一个列表中,然后对其排序,再根据这个生成一个新的链表作为答案。这样的复杂度O(nklog(nk))。是慢于上面的小根堆解法的。可是实际结果是 这个远快于上面的小根堆。

我感觉应该是数据量太小,而小根堆的维护操作次数过多导致在小数据量的情况下,实际操作数解法二是远小于小根堆解法一的。(如果我理解错了,请指正一下)

代码:

解法一:小根堆

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class myHeap:
    def __init__(self):
        self.heap = [0] * 10010
        self.index = 0

    def push(self, n):
        p = self.index
        self.index += 1
        self.heap[p] = n

        while p > 0:
            parent = int((p - 1) / 2)
            if self.heap[parent].val > self.heap[p].val:
                t = self.heap[parent]
                self.heap[parent] = self.heap[p]
                self.heap[p] = t
                p = parent
            else:
                break

    def pop(self):
        if self.index < 0:
            return None

        topVal = self.heap[0]
        self.index -= 1
        self.heap[0] = self.heap[self.index]

        p = 0
        l = p * 2 + 1
        r = p * 2 + 2
        while l < self.index:
            if r < self.index and self.heap[r].val < self.heap[l].val:
                l = r
            if self.heap[p].val > self.heap[l].val:
                t = self.heap[p]
                self.heap[p] = self.heap[l]
                self.heap[l] = t
                p = l
                l = p * 2 + 1
                r = p * 2 + 2
            else:
                break
        return topVal

    def size(self):
        return self.index

    
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        h = myHeap()
        for i in lists:
            if not i == None:
                h.push(i)
                
        ans = ListNode()
        p = ans
        
        while h.size() > 0:
            t = h.pop()
            if not t.next == None:
                h.push(t.next)
            p.next = t
            p = t
        
        p.next = None
        return ans.next

解法二:直接排序

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        nums = []
        
        for i in lists:
            p = i
            while not p == None:
                nums.append(p.val)
                p = p.next
        
        nums.sort()
        
        head = ListNode()
        p = head
        
        for i in nums:
            p.next = ListNode()
            p = p.next
            p.val = i
        
        return head.next

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值