LeetCode-21. 合并两个有序链表

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

注: 本文章使用的代码是Java

LeetCode题链接:合并两个有序链表

官方示例图:

java 复制代码
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    
    }

题解

本题给了两个升序链表,list1和list2。需要我们将题目串起来

画图

那么我们就自己假设出链表模拟模拟。我现在随便画出2个升序链表

模拟过程

我们先不写代码,模拟模拟把两个链表串起来需要怎么做。 很显然,我们会判断 1和2哪个大哪个小,然后发现1 比 2小。所以我们的目光就聚集在1上。

1要往下一个指,随后我们就比较 2和8哪个大哪个小。发现2比8小。那么1指向2所以变成了:

好,现在我们的目光聚集在了2上,我们的2要指向下一个节点。所以我们比较 5 和 8 的大小,发现 5小于8。那么2指向5,所以又变成了:

我们的目光聚集到了5身上,5要指向下一个,那么比较8和7,发现7比8小。那么5指向7,所以又变成了:

现在目光聚集在7身上,7要指向下一个,8和9比较,8比9小,7指向8。变成:

现在目光聚集在8身上,8要指向下一个,24和9比较,9比24小,8指向9。变成:

目光聚集在9上,9要指向下一个,24和10比较,10比较小,9指向10。变成:

目光聚集在10,10要指向下一个,12和24比较,12小,10指向12.变成:

到这里目光聚集在12上,然后list2已经遍历完了,所以我们会让12指向24即可,完成本题。变成:

最后上面这个链表就是我们的答案。

分析总结

我们回过头看看刚才的模拟过程,我们做了什么?提取关键字

  • 目光聚集
  • 比大小
  • 指向

然后就是循环循环.......

最后有一个链表 如:list2遍历完了,我们就指向list的24节点即可。

我们尝试转换成代码逻辑。

转成代码

我们可以先弄出主要的过程,再回过头注意细节。根据刚才的过程:

  • 目光聚集
  • 比大小
  • 指向
  • 目光重新聚集
  • 循环

目光聚集

我们一开始需要把目光放到一个节点上,然后这个节点是怎么出来的呢,不妨回过头看看刚才的模拟过程。 一开始比较两个链表的头节点,谁小目光就在谁身上。

这时产生了代码 目光 = 值小的 即:

ListNode sight = list1.val >= list2.val ? list2 : list1;

比大小和指向

目光聚集锁定后,就要比较大小了,比完就指向下一个。

比较大小: 谁和谁比,比什么。目光聚集在1的时候,是2和8比,比较这两个节点的val

而且是不同目光聚集,比的节点也不一样。于是这需要变量代表这两个比较的节点。而且这两个节点,一个在list1中,一个在list2中。那么有

一个节点 = 目光聚集.next

另一个节点 = 刚才没有被目光聚集那个节点

Java 复制代码
    ListNode cur1 = sight.next;
    ListNode cur2 = sight == list1 ? list2 : list1;

并且在慢慢遍历链表。于是代码变成: 指向 = list1中节点 和 list2中节点 值小的那个 指向执行完之后,被指的那个节点需要向下移动一个(慢慢遍历)。 即:

Java 复制代码
    if(cur1.val <= cur2.val){ 
        sight.next = cur1;
        cur1 = cur1.next;
    } else{
        sight.next = cur2;
        cur2 = cur2.next;
    }
    sight = sight.next;  // 重新聚集目光

循环和打破循环后

回到刚才模拟过程,打破循环的是什么情况呢?对,就是list2遍历完了,他下面没有了。于是就打破循环了。

也就是 list2 == null 那不一定是每次都是list2先遍历完(只不过我们的例子是list2先遍历完),下次也有可能是list1。所以应该是 list1 或者list2 遍历完,没有了就打破循环。

代码: while( list1 !=null || list2 != null)

循环打破之后还没完,我们干了什么事情呢?

当时我们把12(目光聚集sight)的下一个(next)直接指向了24(比较大小的节点cur1)。

发现:list2遍历完了就是 指向cur1。 因为我们这个例子是list2先遍历完。 所以如果是 list1先遍历完 那指向cur2,list2先遍历完指向cur1.

代码:sight.next = cur1 == null ? cur2 : cur1;

返回值

最后我们是需要返回一个链表的。这个链表我们不妨叫 ans。 那么最后返回肯定是 return ans;

我们返回的就是这个 值为1的Node是吧。因为1小,所以我们把1当成链表头。 那我们有 ans = list1.val >= list2.val ? list2 : list1 ;

最终代码

我们按照声明变量,代码逻辑,判断循环即可生成最终代码。

Java 复制代码
  public ListNode mergeTwoLists(ListNode list1, ListNode list2) {

        if(list1 == null || list2 == null){
            return list1 == null ? list2 : list1;
        }
        ListNode ans = list1.val >= list2.val ? list2 : list1;
        ListNode cur1 = ans.next;
        ListNode cur2 = ans == list1  ? list2 :list1;
        ListNode pre = ans;
        
        while(cur1 != null && cur2 != null){
            if(cur1.val >= cur2.val){
                pre.next = cur2;
                cur2 = cur2.next;
            }else{
                pre.next = cur1;
                cur1 = cur1.next;
            }
            pre = pre.next;
        }
        pre.next = cur1 == null ? cur2 : cur1;
        return ans;
    }

时空复杂度

时间复杂度:O(m + n)

其中m和n分别是两个链表的长度。这是因为在最坏的情况下,我们需要遍历两个链表的所有节点。每次循环,我们都会将一个节点从输入链表移动到输出链表,直到两个输入链表都为空。因此,总的操作次数是m + n,所以时间复杂度是O(m + n)。

空间复杂度:O(1)

因为它只使用了几个指针,并没有使用额外的空间来存储数据。

相关推荐
excel10 小时前
Nginx 与 Node.js(PM2)的对比优势及 HTTPS 自动续签配置详解
后端
bobz96511 小时前
vxlan 为什么一定要封装在 udp 报文里?
后端
bobz96511 小时前
vxlan 直接使用 ip 层封装是否可以?
后端
郑道13 小时前
Docker 在 macOS 下的安装与 Gitea 部署经验总结
后端
3Katrina13 小时前
妈妈再也不用担心我的课设了---Vibe Coding帮你实现期末课设!
前端·后端·设计
汪子熙13 小时前
HSQLDB 数据库锁获取失败深度解析
数据库·后端
高松燈13 小时前
若伊项目学习 后端分页源码分析
后端·架构
没逻辑14 小时前
主流消息队列模型与选型对比(RabbitMQ / Kafka / RocketMQ)
后端·消息队列
倚栏听风雨14 小时前
SwingUtilities.invokeLater 详解
后端
Java中文社群14 小时前
AI实战:一键生成数字人视频!
java·人工智能·后端