leetcode 23[python3]几种方法的思考与总结

news2024/12/26 21:19:40

题目

给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例

  • 输入:
    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

题解

题目很好理解,其实方法也很好实现,但是对于优先队列的方法还是有一些疑惑。这个题目的题意很容易理解,该题目的主要有两类方法,一类是直接利用有序队列存储所有有序列表的值,然后基于有序队列构建一个新的满足要求的链表,该方法有些取巧;一类是把多链表合并转换成两个有序链表合并的问题(leetcode 21),该类方法还可以利用分治方法进行优化。下面分别对每类方法及其复杂性进行详细分析。

方法1

方法1 明确要根据k个有序链表构建一个有序列表。首先想到的是遍历k个链表的值逐一的加入到一个list中,然后对list进行降序排序。再从队尾逐个取数构建一个新链表。这个方法比较容易想到,而且比较好实现。代码如下:

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        #新建一个list存储所有链表的值
        heap=[]
        #遍历lists中所有链表的值,逐个加入到heap列表中
        for sub_list in lists:
            while sub_list:
                heap.append(sub_list.val)
                sub_list=sub_list.next
        #对heap进行降序排序
        heap.sort(reverse=True)
        #新建一个链表
        head=ListNode(None)
        curr_list=head
        while heap:
            #从heap列表的尾部取最小数加入到链表中
            temp_list=ListNode(heap.pop())
            curr_list.next=temp_list
            curr_list=curr_list.next
        return head.next

复杂性分析:

假设k个链表中每个链表的长度最长为n。

  • 时间复杂性:时间复杂主要有三块,一部分是遍历k个链表的所有值时间复杂性为 O ( k n ) O(kn) O(kn),然后是排序复杂性为 O ( k n ∗ l o g ( k n ) ) O(kn*log(kn)) O(knlog(kn)),最后构建新链表的复杂性相当于遍历一遍有序链表复杂性也为 O ( k n ) O(kn) O(kn)。因此整个算法的时间复杂性为 O ( k n ∗ l o g ( k n ) ) O(kn*log(kn)) O(knlog(kn))
  • 空间复杂性:空间复杂性主要是新构建的链表空间复杂性为 O ( k n ) O(kn) O(kn)

方法1的优化(官方有序列表)

官方代码中关于有序列表的介绍这里采用heapq来实现有序列表,也就是把上面方法的列表用heapq最小堆来实现。整体思路没有大的变化,代码如下:

class Solution:

    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        import heapq
        #把lists所有数据入heapq
        heap=[]
        for list_1 in lists:
            while list_1:
                heapq.heappush(heap,list_1.val)
                list_1=list_1.next
        #从heapq中逐个取数据构建有序链表
        curr_list=ListNode(None)
        head=curr_list
        while heap:
            #逐个取最小的数据
            temp_node=ListNode(heapq.heappop(heap),None)
            curr_list.next=temp_node
            curr_list=curr_list.next
        return head.next

复杂性分析:

假设k个链表中每个链表的长度最长为n。

  • 时间复杂性:时间复杂主要有两块,一部分是构建heapq的最小堆,复杂性为 O ( k n ∗ l o g ( k n ) ) O(kn*log(kn)) O(knlog(kn)),然后构建新链表的复杂性相当于遍历一遍有序链表复杂性也为 O ( k n ) O(kn) O(kn)。因此整个算法的时间复杂性为 O ( k n ∗ l o g ( k n ) ) O(kn*log(kn)) O(knlog(kn))。可以看到利用heapq与前面直接使用list再排序其实复杂性是一样的。
  • 空间复杂性:空间复杂性主要是新构建的链表空间复杂性为 O ( k n ) O(kn) O(kn)

方法1 优化2

最小堆其实还有一种线性复杂度的构建方法heapify。下面是代码实现:

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        #使用一个队列存储
        heap=[]
        for sub_list in lists:
            while sub_list:
                heap.append(sub_list.val)
                sub_list=sub_list.next
        #对heapify进行堆排序
        import heapq
        heapq.heapify(heap)
        print(heap)
        #heap.sort(reverse=True)
        head=ListNode(None)
        curr_list=head
        while heap:
            temp_list=ListNode(heapq.heappop(heap))
            curr_list.next=temp_list
            curr_list=curr_list.next
        return head.next

复杂性分析:

假设k个链表中每个链表的长度最长为n。

  • 时间复杂性:时间复杂主要有三块,一部分是遍历k个链表的所有值时间复杂性为 O ( k n ) O(kn) O(kn),然后是利用heapify构建最小堆复杂性为 O ( k n ) O(kn) O(kn),最后构建新链表的复杂性相当于遍历一遍有序链表复杂性也为 O ( k n ) O(kn) O(kn)。因此整个算法的时间复杂性为 O ( k n ) O(kn) O(kn)。这里复杂性有明显提升,但是跑的效果跟前面两种方法没有本质区别。
  • 空间复杂性:空间复杂性主要是新构建的链表空间复杂性为 O ( k n ) O(kn) O(kn)

方法2:

在介绍k个有序链表的合并前需要简单介绍下两个链表的有序合并(leetcode21)方法。如果不考虑时间复杂性与空间复杂性实现的方法比较简单,这里要求时间复性为 O ( n ) O(n) O(n),空间复杂性为 O ( 1 ) O(1) O(1)。以示例中的前两个链表为例来说明相关过程。
首先,需要定义一个head链表头来作为最终结果链表的表头。

在这里插入图片描述
对比两个链表的当前节点的值,取小的作为head的next。先选择当前的l1加入到head链表中,然后l1跳到下一个节点。

在这里插入图片描述
再比较当前的L1与l2值的大小。
在这里插入图片描述
重复整个过程真到最后:
在这里插入图片描述
代码如下:

class Solution:
    #合并两个链表leetcode 21
    def merge2Lists(self,list1,list2):
        head=ListNode(0)
        curr_list=head
        while list1 and list2:
            if list1.val<list2.val:
                curr_list.next=list1
                list1=list1.next
            else:
                curr_list.next=list2
                list2=list2.next
            curr_list=curr_list.next
        if list1:
            curr_list.next=list1
        if list2:
            curr_list.next=list2
        return head.next

下面基于两个有序列链表的合并方法来实现k个有序链表的合并。方法也很简单,逐个的对k链表两两合并。代码实现如下:

class Solution:
    #合并两个链表leetcode 21
    def merge2Lists(self,list1,list2):
        head=ListNode(0)
        curr_list=head
        while list1 and list2:
            if list1.val<list2.val:
                curr_list.next=list1
                list1=list1.next
            else:
                curr_list.next=list2
                list2=list2.next
            curr_list=curr_list.next
        if list1:
            curr_list.next=list1
        if list2:
            curr_list.next=list2
        return head.next



    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        n=len(lists)
        ans=None
        for i in range(n):
            ans=self.merge2Lists(ans,lists[i])
        return ans

复杂性分析:

  • 时间复杂性:这里的时间复杂性相跟两个链表合并的长度有关,还是假设k为链表个数,n为每个链表的长度,第一次合并后,ans的长度为n,第二次合并后,ans的长度为2n,第i次合并ans的长度是i*n。因此总的代价为所有之和 O ( k 2 ∗ n ) O(k^2*n) O(k2n)
  • 空间复杂度与两个链表一样,没有额外的空间资源占用 ( 1 ) (1) (1)

方法2优化

显示两两链表依次合并复杂性可以利用分治进行优化,可以减少一定的复杂性。官方有图比较明确这里就不再细说。代码如下:

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    #合并两个链表leetcode 21
    def merge2Lists(self,list1,list2):
        head=ListNode(0)
        curr_list=head
        while list1 and list2:
            if list1.val<list2.val:
                curr_list.next=list1
                list1=list1.next
            else:
                curr_list.next=list2
                list2=list2.next
            curr_list=curr_list.next
        if list1:
            curr_list.next=list1
        if list2:
            curr_list.next=list2
        return head.next

    def dac(self,lists,l,r):
        if l==r:
            return lists[l]
        if l>r:
            return None
        m=(l+r)//2
        return self.merge2Lists(self.dac(lists,l,m),self.dac(lists,m+1,r))



    def mergeKLists(self, lists: List[ListNode]) -> ListNode:
        n=len(lists)
        if n==0:
            return None
        if n==1:
            return lists[0]
        return self.dac(lists,0,len(lists)-1)

这里需要注意的是边界的两种特殊情况,一种是列表是空,一种是k个链表为空的情况。

复杂性分析:

  • 时间复杂性:第一轮合并 k / 2 k/2 k/2​ 组链表,每一组的时间代价是 O ( 2 ∗ n ) O(2*n) O(2n),第二轮是合并 k / 4 k/4 k/4​ 组链表,每一组的时间代价是 O ( 4 ∗ n ) O(4*n) O(4n)。依次所有的时间代价之处为O(kn*logk)$
  • 空间复杂度与两个链表一样,没有额外的空间资源占用 O ( l o g k ) O(logk) O(logk)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/138523.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

⚡️【linux】linux编辑器-VIM的高频使用,快快收藏起来!

⚡️目录 1️⃣VIM最小集 2️⃣VIM指令集 3️⃣VIM的配置 &#x1f332;前言&#xff1a;VIM和VI的区别简单点来讲&#xff0c;他们都是多模式编辑器&#xff0c;不同的是VIM是VI的升级版本&#xff0c;它不仅兼容VI的所有指令&#xff0c;而且还有一些新的特征在里面。例如语…

录屏软件哪个好?试试这6款录屏软件,小编亲测(2023已更新)

对于很多小伙伴来说&#xff0c;电脑录屏是经常需要使用的技能。面对网络上眼花缭乱的录屏软件&#xff0c;究竟哪个录屏软件好用呢&#xff1f;录屏软件哪个好&#xff1f;今天&#xff0c;小编分享亲自测试的这6款录屏软件给你&#xff0c;一起来看看吧。 录屏软件1&#xff…

DevSecOps“内置安全保护”,让软件研发“天生健康”

前言 随着DevOps的发展&#xff0c;DevOps大幅提升了企业应用迭代的速度。但同时&#xff0c;安全如果不能跟上步伐&#xff0c;不仅会抵消DevOps变革带来的提升&#xff0c;拖慢企业数字化转型进程&#xff0c;还会导致漏洞与风险不约而至。所以安全能力在全球范围内受到的重…

4款宝藏国产软件,装了就舍不得卸载,白嫖必备

提到国产软件&#xff0c;许多人想到“流氓、捆绑、广告多”&#xff0c;事实上国产良心软件非常多&#xff0c;如下面几款&#xff0c;每一个都功能强大&#xff0c;最重要还免费使用。 1、原本&#xff08;图片处理神器&#xff09; 日常生活与工作中&#xff0c;经常需要拍摄…

C++单例模式 : 懒汉模式 与 饿汉模式

单例模式&#xff1a; 只能有一个实例&#xff0c;有懒汉和饿汉区分&#xff0c;实现核心思想&#xff1a; 1.构造函数私有化 2.使用静态函数作为接口来获取类对象 1、懒汉模式&#xff1a; 由调用者实例&#xff0c;多线程情况下会存在线程安全问题&#xff0c;需要加互斥锁进…

寒冬已过,2023抓住IT复苏新机会

随着疫情防控进入新的阶段&#xff0c;2023年经济将逐渐回暖&#xff0c;许多行业也将迎来IT需求复苏的新机会。本期&#xff0c;我们就以互联网&#xff0c;金融和房地产这3个支柱行业近期的实际案例&#xff0c;来说明在在线文档领域的新机会。案例1:某互联网集团A公司&#…

数据结构---二叉树路径问题

二叉树路径问题二叉树所有路径分析JAVA实现力扣提交找到一个和为sum的到达叶子节点的路径分析JAVA实现力扣提交求路径&#xff08;中间一段&#xff09;C实现打印根节点到任意节点的路径JAVA实现二叉树所有路径 257二叉树所有路径 分析 前序遍历二叉树递归实现回溯 深度优先搜…

容器化——Centos下部署最后一版支持Docker的k8s集群

部署版本 首先要确定部署的版本 查询Kubernetes对Docker支持的情况 kubernetes/dependencies.yaml at master kubernetes/kubernetes (github.com) 查询Kubernetes Dashboard对Kubernetes支持的情况 Releases kubernetes/dashboard (github.com) 名称版本kubernetes1.23Doc…

如何对CentOS7配置静态IP

CentOS配置静态IP一、检测配置成功的方法1.虚拟机能访问互联网2.虚拟机和物理机可以相互ping通二、配置步骤1.查看物理机IP2.配置虚拟机网络模式3.设置NAT模式网段4.编辑修改配置文件5.测试三、报错解决方案1.网络重启失败2.ping不通一、检测配置成功的方法 1.虚拟机能访问互联…

MyBatis的五种批量查询

一.直接循环插入 RestController RequestMapping("/mybatis3/user") RequiredArgsConstructor public class UserController {private final IUserService iUserService;GetMapping("/one")public Long one(){return iUserService.add();} }Long add();Se…

intel更新13代桌面处理器产品线,更多核心更贵价格

intel 13代酷睿产品线更新带来了更多低于125w TDP的处理器。intel确认了10个65wTDP的SKU和6个35wTDP的SKU。13代酷睿包含新的Raptor Lake和旧的Alder Lake(Refresh)处理器。因为桌面处理器型号序列比较乱&#xff0c;这是SKU命名方式的列表&#xff1a;1XX00KS – 特别(灰烬)版…

【回答问题】ChatGPT上线了!给我推荐比较流行的音频处理算法模型

给我推荐比较流行的音频处理算法模型 音频处理算法模型有很多种&#xff0c;我给你列举一些比较流行的&#xff1a; 声音分类&#xff1a;用于将声音信号分成不同的类别&#xff0c;比如人声、自然声、机器声等。 声音分离&#xff1a;用于从混合的声音信号中分离出单独的声…

PDF可以转Word吗?3种方法建议收藏

PDF文件是很常见的文件传输格式&#xff0c;大部分的时候我们会将常见的办公文件格式转换成PDF。PDF文件占存较小&#xff0c;传输的速度比较快&#xff0c;而且文件的固定性强&#xff0c;不会轻易发生改动让排版和文本错乱&#xff0c;是很好用的文件格式。但常常对于我们所需…

乐维监控keycloak单点登录实操(上篇)

Keycloak为Web应用和Restful服务提供了一站式的单点登录解决方案&#xff0c;为登录、注册、用户管理提供了可视化管理界面&#xff0c;用户可以借助于该界面来配置符合自身需要的安全策略和进行用户管理。下面让我们来看看乐维监控如何实现keycloak单点登录&#xff0c;乐维ke…

反编译Method(反编译类中的方法)

package com.javase.reflect;import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ResourceBundle;/*** 反编译Method 这里我们暂时还不能获取方法体中的内容* 通过读取属性配置文件来对不同的类进行反编译*/ public class ReflectMethodTe…

【机器学习】李宏毅-预测PM2.5

李宏毅-预测PM2.51 实验目的 巩固课堂所学知识&#xff0c;学习使用Linear Regression中梯度下降预测模型&#xff0c;并将所学运用至实践&#xff0c;根据从空气质量监测网下载的观测数据&#xff0c;使用Linear Regression 预测出空气污染指数(即PM2.5) 的数值。 2 实验要求…

【算法】超详细哈夫曼编码JAVA解释

综合实验报告格式 综合实验题目 一、人员和分工 LenckCuak 二、问题描述和基本要求 1、 用哈夫曼编码设计一个压缩软件&#xff1b; 2、 能对输入的任何类型的文件进行哈夫曼编码&#xff0c;产生编码后的文件——压缩文件&#xff1b; 3、 能对输入的压缩文件进行译码&…

【机器学习】李宏毅-食物图像分类器

李宏毅-食物图像分类器1 实验目的 掌握使用Pytorch的使用方法&#xff1a; Pytorch的安装以及环境搭建Pytorch处理数据Pytorch计算梯度以及搭建神经网络Pytorch训练模型 并使用Pytorch来训练CNN模型&#xff0c;实作一个食物的图像分类器。 2 实验要求 可以使用tensorflow或…

print()函数的使用

一、print()函数共三种类型的使用方法二、代码展示

Excel求解运输问题——以福斯特公司问题为例

目录 1.1 问题 福斯特问题例 1.2 数学模型 1.3 excel求解 第一步&#xff1a;建立一个工作表 第二步&#xff1a;求解器求解 1.1 问题 运输问题通常出现在计划货物配送机从供给地区到达需求地区之间的服务中&#xff0c;一般供给地区货物数量有限&#xff0c;需求地区货物…