Python算法题集_合并K个升序链表

 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. 题目解析

- 题意分解

  1. 本题为对多个排序链表进行合并
  2. 基本的解法是双层循环,外循环次数为所有元素个数,内循环为K次比较

- 优化思路

  1. 通常优化:减少循环层次

  2. 通常优化:增加分支,减少计算集

  3. 通常优化:采用内置算法来提升计算速度

  4. 分析题目特点,分析最优解

    1. 可以考虑采用堆结构来进行排序

    2. 可以采用分治法对多链表进行两两合并,化整为零,递归处理

    3. 可以用列表排序法进行简单排序


- 测量工具

  • 本地化测试说明:LeetCode网站测试运行时数据波动很大,因此需要本地化测试解决这个问题
  • CheckFuncPerf(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块
  • 本题本地化超时测试用例自己生成,详见【最优算法章节】

3. 代码展开

1) 标准求解【双层循环】

外层循环,内层最小值检测,依次将最小节点连接起来,性能自然不是最求目标

勉强通关,超过07%Python算法题集_合并K个升序链表_第1张图片

import CheckFuncPerf as cfp

class Solution:
 @staticmethod
 def mergeKLists_base(lists):
     ilen = len(lists)
     if ilen < 1:
         return None
     res_head = ListNode(0)
     tmpNode = res_head
     blast = True
     while blast:
         blast, bNone = False, True
         for iIdx in range(ilen):
             if lists[iIdx]:
                 if bNone:
                     minVal, minidx, bNone = lists[iIdx].val, iIdx, False
                 else:
                     if lists[iIdx].val < minVal:
                         minVal, minidx, bNone = lists[iIdx].val, iIdx, False
         if not bNone:
             blast = True
             tmpNode.next = lists[minidx]
             lists[minidx] = lists[minidx].next
             tmpNode = tmpNode.next
     return res_head.next

result = 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 cfp

class Solution:
 @staticmethod
 def mergeKLists_ext1(lists):
     ilen = len(lists)
     if ilen < 1:
         return None
     list_node = []
     for iIdx in range(ilen):
         while lists[iIdx]:
             list_node.append([lists[iIdx].val, lists[iIdx]])
             lists[iIdx] = lists[iIdx].next
     sort_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 = None
     return 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%Python算法题集_合并K个升序链表_第2张图片

import CheckFuncPerf as cfp

class Solution:
 @staticmethod
 def mergeKLists_ext2(lists):
     import heapq
     res_head = ListNode(0)
     tmpNode = res_head
     buffmin = []
     for iIdx in range(len(lists)):
         if lists[iIdx] :
             heapq.heappush(buffmin, (lists[iIdx].val, iIdx))
             lists[iIdx] = lists[iIdx].next
     while buffmin:
         minval, minidx = heapq.heappop(buffmin)
         tmpNode.next = ListNode(minval)
         tmpNode = tmpNode.next
         if lists[minidx]:
             heapq.heappush(buffmin, (lists[minidx].val, minidx))
             lists[minidx] = lists[minidx].next
     return res_head.next

result = cfp.getTimeMemoryStr(Solution.mergeKLists_ext1, alists)
print(result['msg'], '执行结果 = {}'.format(result['result'].val))

# 运行结果
函数 mergeKLists_ext2 的运行时间为 958.21 ms;内存使用量为 4.00 KB 执行结果 = 0

4) 改进版三【分区海选】

使用递归设计,每次合并两个链表,直到最后合成总链表;这种方式类似分区海选,不是每个选手都要面对所有人,效率最高,资源最少

性能卓越,超越95%Python算法题集_合并K个升序链表_第3张图片

import CheckFuncPerf as cfp

class Solution:
 @staticmethod
 def 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.next
             else:
                 tmpNode.next = link2
                 link2 = link2.next
             tmpNode = tmpNode.next
         tmpNode.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) // 2
         leftlink = mergeSort(lists, left, mid)  
         rightlink = mergeSort(lists, mid + 1, right)
         return mergeTwoLists(leftlink, rightlink) 
     ilen = len(lists)
     if ilen < 1:
         return None
     return 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 = head
    for num in data:
        new_node = ListNode(num)
        current_node.next = new_node
        current_node = new_node
    return 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 ~

你可能感兴趣的:(Python,python,算法,链表,leetcode,合并升序链表)