leetcode NO.23 合并K个排序链表 白痴讲解 腾讯精选练习50

题目描述

在这里插入图片描述

今天做了一道hard的题目,感觉是之前的合并两个有序链表的升级版,提交了一次超时了,后来请教了梁老师改了一次节省了一些读取时间终于过了,同时也了解堆的用法,分治的算法虽然感觉没有完全弄懂,但是还是可以说明一下。

先放上最开始的版本:

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

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        l = ListNode(0)
        p = l
        while lists:
            a = []
            for i in lists:
                while i:
                    a.append(i.val)
            s = a.index(min(a))
            p.next = ListNode(a[s])
            p = p.next
            lists[s] = lists[s].next
        return l.next

这是受到合并两个有序链表的启发,思路是什么呢?回顾一下,两个有序链表的时候是比较两个表头,谁小谁进入新的链表,同时被选中的链表的表头移出原来所在的链表,然后遍历完所有的链表元素,同理现在有K个,我们只需要比较K个链表的表头,找到最小的那个表头就可以了。

现在来解释一下我的代码,首先用a = [ ],来记录这K个表头,s = a.index(min(a)),用s记录列表中最小元素的下标,p.next = ListNode(a[s]),更新新的链表,lists[s] = lists[s].next,原链表去掉被选取的表头,while lists,知道lists中所有元素都被去除。(因为链表的最后一位是None)

思路其实很简单,但是这种方法会超时,为什么会超时呢?因为这里多用了一个空list a [ ]读取K个链表的表头,所以会慢,导致超时。于是不加存储直接提取最小表头就勉强不会超时,但是时间也很长,改进的代码如下:

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

class Solution:
    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        def function(s):
    		result=ListNode(0)
    		z=result
    		loc=0
    		sset=set(range(len(s)))
    		for i in range(len(s)):
        		if s[i] is None:
            		sset.remove(i)
    			while len(sset)>0:
        			minv=float("inf")
        			for i in sset:
            			if s[i].val<minv:
                			minv=s[i].val
                			loc=i
        			z.next=ListNode(minv)
        			z=z.next
        			if s[loc].next is None:
            			sset.remove(loc)
        			else:
            			s[loc]=s[loc].next
    		return result.next
        return function(lists)

参考了题解之后,我发现是需要用堆排序来做会速度很多,但是从原理上讲,这两个算法的时间复杂度是差不多的,只是因为堆结构在python内是C++内置好的结构,所以运行速度会有所提升。

关于堆的概念,就是根节点必定小于子节点,其实就一棵带有大小关系的二叉树,可以在网上找一下。

# 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 function(s):
            result=ListNode(0)
            z=result
            h=[]
            for i in range(len(s)):
                if s[i] is not None:
                	#用一个二维数组去记录表头值和是第几个链表的表头
                    h.append([s[i].val,i])
            #建堆
            heapq.heapify(h)
            while len(h)>0:
            	#弹出堆上的最小值
                tmp=heapq.heappop(h)
                z.next=ListNode(tmp[0])
                z=z.next
                if s[tmp[1]].next is not None:
                    s[tmp[1]]=s[tmp[1]].next
                    #堆上插入被弹出表头的链表的下一个表头
                    heapq.heappush(h,[s[tmp[1]].val,tmp[1]])
            return result.next
            
        return function(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:
        if not lists:return 
        n = len(lists)
        return self.merge(lists, 0, n-1)
    def merge(self,lists, left, right):
        if left == right:
            return lists[left]
        mid = left + (right - left) // 2
        l1 = self.merge(lists, left, mid)
        l2 = self.merge(lists, mid+1, right)
        return self.mergeTwoLists(l1, l2)
    def mergeTwoLists(self,l1, l2):
        if not l1:return l2
        if not l2:return l1
        if l1.val < l2.val:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2

这个方法可以说是非常的精髓了,完美运用上了之前的合并两个有序链表那个题目。
我们看一下这个mergeTwoLists就是之前合并两个有序链表的答案,我们知道了这个函数的功能是合并两个有序链表。
OK,那这个merge函数是在干什么呢?
首先我们看到,它的输入是lists,left,right
lists是K个链表储存在lists这个列表中
left,right是指从第left个链表开始合并到第right个链表

举个例子让大家好理解,比如lists = [1,2,3,4]
这个1,2,3,4只是一个编号,代表的是一个链表

return self.merge(lists, 0, n-1)

这里就是开始调用merge(lists,0,3)
mid = 0 + (3-0)//2 = 1
l1 = merge(lists,0,1)
mid_l1 = 0 + (1-0)//2 = 0

if left == right:
	return lists[left]

l1_1 = merge(lists,0,0) = lists[0]
l1_2 = merge(lists,1,1) = lists[1]

return self.mergeTwoLists(l1, l2)

l1 = 由lists[0],lists[1]合并的有序链表
同理
l2 = merge(lists,2,3)
mid_l2 = 2 + (3-2)//2 = 2
l2_1 = merge(lists,2,2) = lists[2]
l2_2 = merge(lists,3,3) = lists[3]
l2 = 由lists[2],lists[3]合并的有序链表

最后

return self.mergeTwoLists(l1, l2)

return的就是由l1,l2,合并的有序链表,等价于由lists[0],lists[1],lists[2],lists[3]合并的有序链表

再如lists = [1,2,3,4,5]这种个数为奇数的情况
mid = 0 + (4-0)//2 = 2
l1 = merge(lists,0,2)
mid_l1 = 0 + (2-0)//2 = 1
l1_1 = merge(lists,0,1)
mid_l1_1 = 0 + (1-0)//2 = 0
l1_1_1 = merge(lists,0,0) = lists[0]
l1_1_2 = merge(lists,1,1) = lists[1]
所以l1_1 = 由lists[0],lists[1]合并的有序链表
l1_2 = merge(lists,2,2) = lists[2]
所以l1 = 由l1_1,l1_2合并的有序链表,等价于由lists[0],lists[1],lists[2]合并的有序链表

l2 = merge(lists,3,4)
mid_l2 = 3 + (4-3)//2 = 3
l2_1 = merge(lists,3,3) = lists[3]
l2_2 = merge(lists,4,4) = lists[4]
所以l2 = 由lists[3],lists[4]合并的有序链表

所以return的是由lists[0],lists[1],lists[2]lists[3],lists[4]合并的有序链表

所以我们可以看到这个merge函数就是利用二分将所有left right之间的链表全部连起来,而且是两两相连。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值