21. 合并两个有序链表

· 浏览次数 : 0

小编点评

题目链接一:将两个升序链表合并为一个新的升序链表。 1. 输入:l1 = [1,2,4], l2 = [1,3,4],输出:[1,1,2,3,4,4] 2. 输入:l1 = [], l2 = [],输出:[] 3. 输入:l1 = [], l2 = [0],输出:[0] 提示:两个链表的节点数目范围是[0, 50];l1 和 l2 均按非递减顺序排列;-100 <= Node.val <= 100 解题思路: 1. 递归法 1.1 解题思路:相同值先存入结果链表,然后比较新的值; 函数主体:如果listNode1的值小于listNode2的值,则把listNode1的下一个节点作为新的结果链表的起始节点传入递归函数。 终止条件:当其中一个链表为空时,将另一个链表的剩余部分插入结果链表的末尾。 1.2 复杂度分析:时间复杂度O(m+n),空间复杂度O(m+n)。 2. 迭代法 2.1 解题思路:双指针指向较小组的节点,先将它们的值存入结果链表,然后将较大的值指向结果链表的末尾; 函数主体:使用两个指针分别指向上游节点和下游节点,在遍历过程中比较它们的值。 终止条件:当其中一个链表为空时,将另一个链表的剩余部分插入结果链表的末尾。 2.2 复杂度分析:时间复杂度O(m+n),空间复杂度O(1)。

正文

题目链接

一、题目描述

1. 题目

将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

2. 示例

示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:
输入:l1 = [], l2 = []
输出:[]

示例 3:
输入:l1 = [], l2 = [0]
输出:[0]

3. 提示

  • 两个链表的节点数目范围是[0, 50]
  • l1 和 l2 均按非递减顺序排列
  • -100 <= Node.val <= 100

二、代码实现

1. 递归

1.1 解题思路

链表、树、图相关的算法首先想递归。递归相关的知识见我的另一篇文章迭代与递归

递归设计函数的步骤:
1. 找重复: 找到的相同的子问题。

如下图,可以得出子问题为,l1 和 l2 当前节点的不断比较,当然,这里的当前节点是需要不断变化的。

2. 找变化:聚焦于某一个子问题,查看变化的量,通常会作为参数,这时可定义函数体。

由于当前节点的比较是不断变化的,所以这里变化的量我们就需要分情况讨论了,如下图,可总结为小的往前走一步,继续跟大的比

总结完情况,则进行代码实现:

public ListNode mergeTwoLists (ListNode listNode1, ListNode listNode2) {
     if(listNode1.val < listNode2.val){
         listNode1.next = mergeTwoLists(listNode1.next,listNode2);
         return  listNode1;
     }else {
         listNode2.next = mergeTwoLists(listNode1,listNode2.next);
         return  listNode2;
     }
 }

3. 找出口: 也就是找终止条件。

什么时候无法比较当前节点的值,这个比较好找,就是当 listNode1 的为 null,或者 listNode2 为 null 时,无法进行比较了,可直接返回对应有值的节点,代码如下。

 public ListNode mergeTwoLists (ListNode listNode1, ListNode listNode2) {
    if (listNode1 == null) {
        return listNode2;
    } else if (listNode2 == null) {
        return listNode1;
    } else if (listNode1.val < listNode2.val) {
        listNode1.next = mergeTwoLists(listNode1.next, listNode2);
        return listNode1;
    } else {
        listNode2.next = mergeTwoLists(listNode1, listNode2.next);
        return listNode2;
    }
}

1.2 复杂度分析

时间复杂度 O(m+n),m、n 分别为链表的长度,因为每次调用递归都会去掉 listNode1 或者 listNode2 的头节点(直到至少有一个链表为空),则最多只会递归调用每个节点一次。因此,时间复杂度取决于合并后的链表长度,即 O(m+n)。
空间复杂度 O(m+n),m、n 分别为链表的长度,递归需要消耗栈空间且大小取决于递归调用的深度。结束递归调用时最多调用 m+n 次,因此空间复杂度为 O(m+n)。

2. 迭代

2.1 解题思路

迭代与递归基本都是可以互转的,与递归类似,我们将两个链表的比较理解为指针的移动。

这里我们还需要一个哨兵节点,为什么需要哨兵节点呢,我们在遍历链表的过程中是不断的调整它的 next 指针,当我们遍历到最后时,哨兵节点更容易返回合并后的链表,过程如下图。

通过图解,可以了解迭代的大致流程,但是我们在 Java 代码实现时,还需要维护一个 prev 指针,用来调整它的 next 指针。

为什么需要这个指针而不是直接返回哨兵节点呢?原因是我们在不断的遍历中,指针会不断的下移,当我们合并完成所有的节点时,此时的指针是在最后,这时我们在获取合并后的链表就比较麻烦了。

而用一个 prev 指针代替哨兵节点去进行 next 指针的移动,当返回链表时,只需返回哨兵节点的 next 即可。

代码实现如下:

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    
    // 哨兵节点的设置
    ListNode prehead = new ListNode(-1);

    // 维护 prev 指针进行移动
    ListNode prev = prehead;

    
    while (list1 != null && list2 != null) {
        // 值比较,小的接入哨兵节点,并往前走一步,继续与大的比较
        if (list1.val < list2.val) {
            prev.next = list1;
            list1 = list1.next;
        }
        else {
            prev.next = list2;
            list2 = list2.next;
        }
        
        // 指针移动到 next,为了后续的节点接入
        prev = prev.next;
    }

    // 三元运算,当 list1 或 list2 出现 null 时,非 null 的则可以直接接入哨兵节点
    prev.next = list1 != null ? list1 : list2;
    return prehead.next;
}

2.2 复杂度分析

时间复杂度 O(m+n),m、n 分别为链表的长度,合并操作只需遍历链表。
空间复杂度 O(1),prehead、prev 均只使用常数大小的内存空间。

与21. 合并两个有序链表相似的内容:

21. 合并两个有序链表

题目链接 一、题目描述 1. 题目 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 2. 示例 示例 1: 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 示例 2: 输入:l1 = [], l2 = []

记一次 .NET 某仪器测量系统 CPU爆高分析

一:背景 1. 讲故事 最近也挺奇怪,看到了两起 CPU 爆高的案例,且诱因也是一致的,觉得有一些代表性,合并分享出来帮助大家来避坑吧,闲话不多说,直接上 windbg 分析。 二:WinDbg 分析 1. CPU 真的爆高吗 这里要提醒一下,别人说爆高不一定真的就是爆高,我们一定要拿数据说话,可以

MySQL的驱动表与被驱动表

驱动表与被驱动表的含义 在MySQL中进行多表联合查询时,MySQL会通过驱动表的结果集作为基础数据,在被驱动表中匹配对应的数据,匹配成功合并后的临时表再作为驱动表或被驱动表继续与第三张表进行匹配合并,直到所有表都已匹配完毕,最后将结果返回出来。匹配算法:Nested-Loop Join(嵌套循环连

2024 Selenium10个替代品

随着自动化测试需求的不断增长,Selenium作为广泛使用的自动化测试工具,虽然功能强大,但也存在一些限制和挑战。在2024年, 越来越多的替代工具涌现,它们提供了更高效、更易用的解决方案。那么,哪些替代品值得我们关注呢? 在自动化测试领域,除了Selenium,还有哪些工具能够满足我们的需求,并且

【数据集】Maple-IDS——网络安全恶意流量检测数据集

一、数据集介绍 Maple-IDS数据集是一个网络入侵检测评估数据集,旨在增强异常基础入侵检测系统(IDS)和入侵预防系统(IPS)的性能和可靠性。随着网络空间安全领域攻击的日益复杂化,拥有一个可靠和最新的数据集对于测试和验证IDS和IPS解决方案至关重要。 数据集由东北林业大学网络安全实验室发布,

Linux多线程

Linux多线程,线程的基本概念,线程库的基本原理,线程私有栈的由来,互斥与同步,互斥锁,信号量,条件变量,线程池,生产者消费者模式,基于阻塞队列/阻塞环形队列的生产者消费者模模型,单例模式,饿汉懒汉方式

使用ML.NET训练一个属于自己的图像分类模型,对图像进行分类就这么简单!

前言 今天大姚给大家分享一个.NET开源、免费、跨平台(支持Windows、Linux、macOS多个操作系统)的机器学习框架:ML.NET。并且本文将会带你快速使用ML.NET训练一个属于自己的图像分类模型,对图像进行分类。 ML.NET框架介绍 ML.NET 允许开发人员在其 .NET 应用程序

Linux 中 WIFI 和热点的使用

之前一直在 ubuntu 的图形界面中使用,突然需要在 ARM 板上打开热点,一时给弄蒙了,在此记录一下 一、网卡命令 显示所有网络信息 sudo ip link show 关闭或打开网络 sudo ip link set wlan0 down sudo ip link set wlan0 up 激

某手创作服务 __NS_sig3 sig3 | js 逆向

拿获取作品列表为例 https://cp.kuaishou.com/rest/cp/works/v2/video/pc/photo/list?__NS_sig3=xxxxxxxxxxx 搜索__NS_sig3 发现__NS_sig3是一个异步回调生成的值 s().call("$encode", [i

【VMware vCenter】VMware vCenter Server(VCSA) 5.5 版本证书过期问题处理过程。

之前帮客户处理了一个因证书过期导致 vCenter Server 无法登录的问题,在此记录一下,因为时间过去有点久了,可能会有些地方描述的不是很清楚,所以就当作参考就行。客户环境是一个非常老的 vCenter Server 5.5 版本并基于 Linux 版本的 VCSA (当时这个版本还有基于 W