力扣21 - 合并两个有序链表【归并排序思维】

news2025/4/3 0:59:52

链式铠甲——合体

  • 一、题目描述
  • 二、思路分析
  • 三、代码详解
    • way1【不带头结点】
    • way2【带头结点】
  • 四、整体代码展示【需要自取】
    • 方法一:不带哨兵位【无头结点】
    • 方法二:带哨兵位【有头结点】
  • 五、总结与提炼

一、题目描述

原题传送门🚪

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

在这里插入图片描述

示例 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]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

看完了本题的描述,你是否觉得这个题目在哪里做到过。是的,对于这道题目,我们曾经有做过类似的力扣习题,而且我也做了题解说明,就是力扣88 - 合并两个有序数组,没看过的同学可以先去看看这个,对于数组的合成比链表简单一些

二、思路分析

好,看完题目的描述,我们来分析一下如何去求解这道题目

  • 思路很简单,合并链表,那就是要将两个链表合为一个,这里没有说最后归并到第一个还是第二个,所以我们需要重新定义一个链表,然后进行尾插结点的操作
  • 一同遍历这两个链表,比较所遍历到的结点,看看那个结点的值小,将小的值链接到新的链表中,然后直到有一个链表遍历到了结尾,就结束两个链表的同时遍历,跳出循环
  • 接着可能有一个链表还没有遍历完,只需要将没遍历完的那个链表继续链在新链表最后即可,因为题目本身说明了给出的链表就是有序的,所以不需要担心

  • 还有一个的话就是带头结点的,对于这种思路的代码写起来简单一些,因为不需要考虑一开始尾插结点的时候尾指针是否为空,只需要将结点做一个尾插即可,具体的分析我们到下一模块讲

三、代码详解

然后我们通过这段代码来给大家分析一下

way1【不带头结点】

  • 首先是一些初始化
ListNode* tail, *newhead;
tail = newhead = NULL;
  • 下面是循环遍历的逻辑,可以看出来,代码量非常得多其实就是在判断是否是第一个尾插的结点
while(list1 && list2)
{
    if(list1->val < list2->val)
    {
        if(tail == NULL)
        {
            tail = newhead = list1;
        }
        else
        {
            tail->next = list1;
            tail = tail->next;
        }
        list1 = list1->next;
    }          
    else
    {
        if(tail == NULL)
        {
            tail = newhead = list2;
        }
        else
        {
            tail->next = list2;
            tail = tail->next;
        }
        list2 = list2->next;
    }
}
  • 首先是不带头结点这一块,刚开始做一个初始化

在这里插入图片描述

  • 然后看看1 = 1,随便拿哪个都可以,我们拿【list2】,然后将其后移,此时【tail】无需动

在这里插入图片描述

  • 然后拿【list1】,list1继续后移,tail后移

在这里插入图片描述

  • 同理

在这里插入图片描述

  • 同理

在这里插入图片描述

  • 然后此时可以看到,两个链表都只剩下最后最后一个结点,我们继续看
  • 可以看到,当这个【list1】遍历完了之后,我们此时应该跳出循环,将【list2】的剩余结点全部链接到【list3】中

在这里插入图片描述

  • 使用的是下面这段代码逻辑
if(list1)
    tail->next = list1;
if(list2)
    tail->next = list2;

在这里插入图片描述

  • 此时也就结束了我们的有序链表合并
  • 最后将这个【List3】指向首个结点的指针返回即可
return newhead;
  • 但是。。。出来的确实这个,这是为什么呢?

在这里插入图片描述

  • 此时你就要仔细分析力扣给出来报错,也就是两个链表:一个为空,一个只有一个0的值。这个时候再回过去看你的代码其实就可以发现,因为有一个链表初始的时候就是空的,所以不会进入上面那段大的循环逻辑,因此会直接进到下面这个最后的尾插,但是可以看到力扣给出的错误定位提示,因为我们在一开始的时候将这个【tail】和【newhead】都设置为NULL了,所以在上面没有修改tail的时候进到这里对tail使用【tail->next】其实就是一个解引用的操作,对空指针进行解引用其实是一个很错误的写法,因此程序给我们报出了错误,那应该怎么去修改呢?

在这里插入图片描述

  • 你可以在后面这段if判断的逻辑里修改,当然也可以在程序一开头就做一个判读,就像下面这样
if(list1 == NULL)
    return list2;
if(list2 == NULL)
    return list1;
  • 可以看到,顺利AC🎈,抬走,下一个

在这里插入图片描述

way2【带头结点】

  • 说完不太头结点的方法,接下去我们来说说带头结点的情况,这种情况的代码可比不太头结点来得简洁多了,因为不需要考虑第一次尾插的结点是否为头结点
  • 首先先来看一下内部的循环逻辑该如何修改。可以看到,当这个结点值的大小判断完后就直接进行了一个尾插,然后就是结点指针的移动,完全不需要另外进行一个判断,代码就看起来很简练
while(list1 && list2)
{
    if(list1->val < list2->val)
    {
        tail->next = list1;
        tail = tail->next;
        list1 = list1->next;
    }          
    else
    {
        tail->next = list2;
        tail = tail->next;
        list2 = list2->next;
    }
}
  • 有关如何进行插入就如下图所示,我就不做分步讲解了,和不带头结点类似,若是比较到哪个结点小,直接尾插在List3之后即可

在这里插入图片描述

  • 有一点比较重要的我讲一下,就是当最后需要返回头结点指针的时候,返回的不是List3,而是【List3->next】,那有同学问,这是为什么呢?我们现在看一看结点指针的初始化
  • 这里新的头结点指针我使用的是newhead,这个没关系,大家可以自己命名,可以看到一开始就为【tail】和【newhead】这两个结构体指针进行了一个初始化操作,也就是在堆区中为其申请出一块空间,但是并没有将它们的【data域】和【next域】进行一个初始化赋值,因为这个是带头结点的,因此不能赋值成NULL,只有不太头的可以这么多,于是这两个域就会变成随机值和随机地址值,不信的话我带你们去DeBug调试看看
ListNode* tail, *newhead;
tail = newhead = (ListNode *)malloc(sizeof(ListNode));
newhead->next = NULL;

在这里插入图片描述

  • 可以看到,当这个两个指针被初始化后是一个随机值,所以后续的结点其实是尾插在这个头结点之后的,因此提交之后就会变成下面这样,这其实就出现了问题,其实我们应该要返回的是当前头结点的下一个结点,才是正确的返回结果

在这里插入图片描述

  • 因此我们可以总结出,对于带头结点的单链表,返回的当前newhead->next,但是这里为了使程序更加完整,我们应该在返回后将这个头结点释放掉,这样就可以将这块申请的内存地址还回去了,程序也显得比较完善
  • 我们也可以总结出来对于带头结点的单链表这个头结点其实只是起到一个辅助尾插的作用,链表真正的结构还是从头结点的下一结点开始
ListNode* nextNode = newhead->next;
free(newhead);      //将新的头结点销毁,返回其下一个结点

return newhead;        //返回原来头结点的下一结点

好,在看完了两种方法之后,相信你对带头结点和不带头结点的单链表应该有了一个很好的认识,下去也要自己多加练习才能融会贯通

四、整体代码展示【需要自取】

给出两种方法的代码

方法一:不带哨兵位【无头结点】

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(list1 == NULL)
            return list2;
        if(list2 == NULL)
            return list1;
            
        ListNode* tail, *newhead;
        tail = newhead = NULL;

        while(list1 && list2)
        {
            if(list1->val < list2->val)
            {
                if(tail == NULL)
                {
                    tail = newhead = list1;
                }
                else
                {
                    tail->next = list1;
                    tail = tail->next;
                }
                list1 = list1->next;
            }          
            else
            {
                if(tail == NULL)
                {
                    tail = newhead = list2;
                }
                else
                {
                    tail->next = list2;
                    tail = tail->next;
                }
                list2 = list2->next;
            }
        }

        if(list1)
            tail->next = list1;
        if(list2)
            tail->next = list2;

        return newhead;
    }
};

方法二:带哨兵位【有头结点】

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        //无需判断其中一个链表是否为空,因为tail不可能为空
        ListNode* tail, *newhead;
        tail = newhead = (ListNode *)malloc(sizeof(ListNode));
        newhead->next = NULL;

        while(list1 && list2)
        {
            if(list1->val < list2->val)
            {
                tail->next = list1;
                tail = tail->next;
                list1 = list1->next;
            }          
            else
            {
                tail->next = list2;
                tail = tail->next;
                list2 = list2->next;
            }
        }

        if(list1)
            tail->next = list1;
        if(list2)
            tail->next = list2;

        ListNode* nextNode = newhead->next;
        free(newhead);      //将新的头结点销毁,返回其下一个结点

        return nextNode;        //返回原来头结点的下一结点
    }
};

五、总结与提炼

  • 好,我们来总结一下本文所学习的知识,在本文中,我们通过一道力扣题【合并两个有序链表】,讲解了对于单链表不带头结点和带头结点的不同做法:对于不带头结点,需要在第一次尾插的时候判断一下尾结点指针是否为空,在后续再进行一个尾插;对于带头结点的单链表,我们在进行结点尾插的时候直接进行尾插即可,但是在最后返回整个链表的头时,不能返回我们开辟出来的头结点指针,而是要返回其next的结点,才是链表真正的开始部分
  • 对于链表带头和不带头大家一定多加练习,在力扣上大多都是不太头的,你要做到的是灵活地修改,将不带头的可以轻松转换为带头的

以上就是本文所要描述的所有内容,感谢您对本文的观看,如有疑问请于评论区留言或者私信我都可以🍀

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

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

相关文章

vs2019编译ffmpeg4.4为静态库或动态库

参考文章&#xff1a;vs2019编译ffmpeg源码为静态库动态库【完整步骤、亲测可行】 文章目录编译测试编译 直接把博主的项目下下来 我打开里面FFmpeg文件发现它貌似是4.4版本 然后照着他给的步骤执行命令 先找到vs2019的命令行工具 然后执行两个脚本 执行以上两个脚本后&…

快速排序和归并排序非递归的详解

在经过主页中《八大排序》&#xff08;下&#xff09;的学习&#xff0c;我们了解了快速排序和归并排序且都是递归的思想&#xff0c;但是如果递归的深度很深呢&#xff1f;这一节我们就引出用非递归的思想解决这个问题。&#x1f635;&#x1f635;&#x1f635; 快速排序非递…

根据给定数组,创建形状相同的数组并且采用不同方式填充full_like()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 根据给定数组&#xff0c;创建形状相同的数组 并且采用不同方式填充 full_like() [太阳]选择题 对下面代码中full_like函数结果描述错误的选项为&#xff1f; import numpy as np print(&q…

谷粒学院——Day05【后台系统前端项目创建、讲师管理模块前端开发】

后台系统前端项目创建 一、vue-element-admin 简介 vue-element-admin 是基于 element-ui 的一套后台管理系统集成方案。 功能&#xff1a;https://panjiachen.github.io/vue-element-admin-site/zh/guide/#功能 GitHub地址&#xff1a;https://github.com/PanJiaChen/vue-ele…

分布式锁_Redis分布式锁+Redisson分布式锁+Zookeeper分布式锁+Mysql分布式锁(原版)

分布式锁_Redis分布式锁Redisson分布式锁Zookeeper分布式锁Mysql分布式锁&#xff08;原版&#xff09; 文章目录分布式锁_Redis分布式锁Redisson分布式锁Zookeeper分布式锁Mysql分布式锁&#xff08;原版&#xff09;1. 传统锁回顾1.1. 从减库存聊起1.2. 环境准备1.3. 简单实现…

Dreamweaver网页设计与制作100例——HTML5期末考核大作业——票务网站整套网页

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…

正确查询DO基站IP

对于DO站的IP地址在系统中设置是否正确需要确定基站侧IP地址和RNC侧地址是否匹配&#xff0c;匹配关系为&#xff1a;基站侧IP地址减2即为RNC侧地址&#xff08;如&#xff1a;RCS 234 BTS-IP: 6.33.84.30 则匹配RNC侧地址即为6.33.84.28&#xff09;&#xff0c;下面举例进行襄…

基于单片机的语音小车设计

目 录 引言 1 1 系统概述 1 1.1 声控产品前景和发展趋势 1 1.2 研究目的和意义 1 1.3 本次设计内容 2 2 系统设计的整体方案 2 2.1 主控芯片的方案论证 2 2.2 语音识别模块的方案论证 3 2.3 电机驱动方案选择 4 2.4 本章小节 4 3 系统…

使用ssh克隆GitHub仓库以及替换https方式

目录 使用ssh克隆GitHub仓库 第一步&#xff1a;生成ssh 第二步&#xff1a;添加SSH key 第三步&#xff1a;验证绑定是否成功 第四步&#xff1a;克隆 意外的情况&#xff1a; 情况1&#xff1a;ssh连接GitHub失败 情况2&#xff1a;使用git clone 不成功 替换原来的…

队列的简单实现

队列的简单实现一、什么是队列二、队列的分类三、队列的数据结构四、队列的基本操作1、初始化队列2、销毁队列3、入队4、出队5、队列判空6、获取队头元素7、获取队尾元素8、获取队列元素总结头文件基本操作一、什么是队列 首先我们既然想要实现队列就得明白什么是队列&#xff…

1.7.4、计算机网络体系结构中的术语

1.7.4、计算机网络体系结构中的术语 1.7.4.1、实体 实体&#xff1a; 任何可发送或接收信息的硬件或软件进程。 对等实体&#xff1a; 收发双方相同层次中的实体 1.7.4.2、协议 协议&#xff1a;控制两个的对等实体进行逻辑通信的规则的集合 之所以称为逻辑通信&#xf…

目标检测论文解读复现之五:改进YOLOv5的SAR图像舰船目标检测

目标检测论文解读复现 文章目录目标检测论文解读复现前言一、摘要二、网络模型及核心创新点三、应用数据集四、实验效果&#xff08;部分展示&#xff09;五、实验结论六、投稿期刊介绍前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改…

HTML5期末考核大作业,电影网站——橙色国外电影 web期末作业设计网页

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…

【代码精读】ATF的异常向量表

快速链接: . 👉👉👉 【代码精读】–Kernel/ATF/optee等-目录👈👈👈 付费专栏-付费课程 【购买须知】:本专栏的视频介绍-----视频👈👈👈概要: 本文概述了ARMv8/ARMv9的aarch64体系中异常向量表的结构、以及基地寄存器的总结。然后通过导读ATF BL31的异常向量…

Flink系列文档-(YY09)-Flink时间语义

1 三种时间语义 在实时流式计算中&#xff0c;"时间"是一个能影响计算结果的非常重要因素&#xff01; 试想场景&#xff1a;每隔1分钟计算一次最近10分钟的活跃用户量&#xff1a; ①假设此刻的时间是13:10&#xff0c;要计算的活跃用户量时间段为&#xff1a;[ …

【C++】类和对象(下)

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;再谈构造…

kindle自定义屏保之自定义字帖

kindle自定义屏保之自定义字帖 01 前言 毕业以后&#xff0c;很少动笔写字了&#xff0c;某天要手写一堆材料&#xff0c;写出来实在不忍直视&#xff0c;于是当晚下班后突发奇想——能不能把一些字帖搞成kindle屏保&#xff0c;摆在桌面上&#xff0c;睡前说不准还能练练 随…

web课程设计 基于html+css+javascript+jquery女性化妆品商城

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

软件测试基础知识

软件测试基础知识1.测试模型2.测试分类3.测试目的与原则4.测试流程5.测试发展规划6.单元测试7.黑盒测试8.白盒测试9.缺陷1.测试模型 瀑布模型 开发将系统都做好了&#xff0c;然后测试。最大问题是测试工作后置&#xff0c;导致整个项目开发完成之后如果发现比较重要的问题&…

基于微信小程序的校运会管理系统设计与实现-计算机毕业设计源码+LW文档

小程序开发说明 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Mave…