Python算法题集_合并K个升序链表
- 题23:合并K个升序链表
- 1. 示例说明
- 2. 题目解析
- - 题意分解
- - 优化思路
- - 测量工具
- 3. 代码展开
- 1) 标准求解【双层循环】
- 2) 改进版一【列表排序】
- 3) 改进版二【堆排序】
- 4) 改进版三【分区海选】
- 4. 最优算法
本文为Python算法题集之一的代码示例
题23:合并K个升序链表
1. 示例说明
-
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [1->4->5,1->3->4,2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6
示例 2:
输入:lists = [] 输出:[]
示例 3:
输入:lists = [[]] 输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i]
按 升序 排列lists[i].length
的总和不超过10^4
2. 题目解析
- 题意分解
- 本题为对多个排序链表进行合并
- 基本的解法是双层循环,外循环次数为所有元素个数,内循环为K次比较
- 优化思路
-
通常优化:减少循环层次
-
通常优化:增加分支,减少计算集
-
通常优化:采用内置算法来提升计算速度
-
分析题目特点,分析最优解
-
可以考虑采用堆结构来进行排序
-
可以采用分治法对多链表进行两两合并,化整为零,递归处理
-
可以用列表排序法进行简单排序
-
- 测量工具
- 本地化测试说明:LeetCode网站测试运行时数据波动很大,因此需要本地化测试解决这个问题
CheckFuncPerf
(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块- 本题本地化超时测试用例自己生成,详见【最优算法章节】
3. 代码展开
1) 标准求解【双层循环】
外层循环,内层最小值检测,依次将最小节点连接起来,性能自然不是最求目标
勉强通关,超过07%
import CheckFuncPerf as cfpclass Solution:@staticmethoddef mergeKLists_base(lists):ilen = len(lists)if ilen < 1:return Noneres_head = ListNode(0)tmpNode = res_headblast = Truewhile blast:blast, bNone = False, Truefor iIdx in range(ilen):if lists[iIdx]:if bNone:minVal, minidx, bNone = lists[iIdx].val, iIdx, Falseelse:if lists[iIdx].val < minVal:minVal, minidx, bNone = lists[iIdx].val, iIdx, Falseif not bNone:blast = TruetmpNode.next = lists[minidx]lists[minidx] = lists[minidx].nexttmpNode = tmpNode.nextreturn res_head.nextresult = cfp.getTimeMemoryStr(Solution.mergeKLists_base, alists)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))# 运行结果
函数 mergeKLists_base 的运行时间为 950.22 ms;内存使用量为 4.00 KB 执行结果 = 0
2) 改进版一【列表排序】
将所有节点均存入列表结构,通过列表排序,最后再连接起来,代码简洁,性能优异,但是内存O(n)
性能卓越,超越96%
import CheckFuncPerf as cfpclass Solution:@staticmethoddef mergeKLists_ext1(lists):ilen = len(lists)if ilen < 1:return Nonelist_node = []for iIdx in range(ilen):while lists[iIdx]:list_node.append([lists[iIdx].val, lists[iIdx]])lists[iIdx] = lists[iIdx].nextsort_list = sorted(list_node, key=lambda x: x[0])for iIdx in range(len(sort_list)-1):sort_list[iIdx][1].next = sort_list[iIdx+1][1]sort_list[-1][1].next = Nonereturn sort_list[0][1]result = cfp.getTimeMemoryStr(Solution.mergeKLists_ext1, alists)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))# 运行结果
函数 mergeKLists_ext1 的运行时间为 913.20 ms;内存使用量为 992.00 KB 执行结果 = 0
3) 改进版二【堆排序】
应用堆队列heapq
来进行最小值检测,代码相对简洁,性能还行
马马虎虎,超过72%
import CheckFuncPerf as cfpclass Solution:@staticmethoddef mergeKLists_ext2(lists):import heapqres_head = ListNode(0)tmpNode = res_headbuffmin = []for iIdx in range(len(lists)):if lists[iIdx] :heapq.heappush(buffmin, (lists[iIdx].val, iIdx))lists[iIdx] = lists[iIdx].nextwhile buffmin:minval, minidx = heapq.heappop(buffmin)tmpNode.next = ListNode(minval)tmpNode = tmpNode.nextif lists[minidx]:heapq.heappush(buffmin, (lists[minidx].val, minidx))lists[minidx] = lists[minidx].nextreturn res_head.nextresult = cfp.getTimeMemoryStr(Solution.mergeKLists_ext1, alists)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))# 运行结果
函数 mergeKLists_ext2 的运行时间为 958.21 ms;内存使用量为 4.00 KB 执行结果 = 0
4) 改进版三【分区海选】
使用递归设计,每次合并两个链表,直到最后合成总链表;这种方式类似分区海选,不是每个选手都要面对所有人,效率最高,资源最少
性能卓越,超越95%
import CheckFuncPerf as cfpclass Solution:@staticmethoddef mergeKLists_ext3(lists):def mergeTwoLists(link1, link2) -> ListNode:res_head = ListNode(0) tmpNode = res_head while link1 and link2: if link1.val < link2.val:tmpNode.next = link1 link1 = link1.nextelse:tmpNode.next = link2link2 = link2.nexttmpNode = tmpNode.nexttmpNode.next = link1 if link1 else link2 return res_head.next def mergeSort(lists, left, right) -> ListNode:if left == right:return lists[left]mid = (left + right) // 2leftlink = mergeSort(lists, left, mid) rightlink = mergeSort(lists, mid + 1, right)return mergeTwoLists(leftlink, rightlink) ilen = len(lists)if ilen < 1:return Nonereturn mergeSort(lists, 0, ilen - 1)result = cfp.getTimeMemoryStr(Solution.mergeKLists_ext3, alists)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))# 运行结果
函数 mergeKLists_ext3 的运行时间为 393.49 ms;内存使用量为 0.00 KB 执行结果 = 0
4. 最优算法
根据本地日志分析,最优算法为第4种mergeKLists_ext3
def generateOneLinkedList(data):head = ListNode()current_node = headfor num in data:new_node = ListNode(num)current_node.next = new_nodecurrent_node = new_nodereturn head.next
def generateOneLinks(listlink):result = []for iIdx in range(len(listlink)):result.append(generateOneLinkedList(listlink[iIdx]))return result
iLen, k = 100000, 10
listnums = []
for iIdx in range(k):tmpNums = [x*(iIdx+1) for x in range(iLen)]listnums.append(tmpNums)
alists = generateOneLinks(listnums)
result = cfp.getTimeMemoryStr(Solution.mergeKLists_base, alists)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))# 算法本地速度实测比较
函数 mergeKLists_base 的运行时间为 950.22 ms;内存使用量为 4.00 KB 执行结果 = 0
函数 mergeKLists_ext1 的运行时间为 913.20 ms;内存使用量为 992.00 KB 执行结果 = 0
函数 mergeKLists_ext2 的运行时间为 958.21 ms;内存使用量为 4.00 KB 执行结果 = 0
函数 mergeKLists_ext3 的运行时间为 393.49 ms;内存使用量为 0.00 KB 执行结果 = 0
一日练,一日功,一日不练十日空
may the odds be ever in your favor ~