当前位置: 首页 > news >正文

力扣最热一百题——合并两个有序链表

目录

题目链接:21. 合并两个有序链表 - 力扣(LeetCode)

题目描述

示例

提示:

解法一:比大小放入

Java写法:

运行时间以及复杂度

C++写法:

运行时间以及复杂度

总结


题目链接:21. 合并两个有序链表 - 力扣(LeetCode)

注:下述题目描述和示例均来自力扣

题目描述

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

示例

示例 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 均按 非递减顺序 排列


解法一:比大小放入

  1. 初始化哨兵节点
    • 创建一个哨兵节点(dummy node),其next指针指向null。这个哨兵节点将作为新链表的临时头部,这样做的好处是可以方便地在链表头部添加元素,而不需要特殊处理头节点为空的情况。
  2. 双指针迭代
    • 使用两个指针list1list2分别指向两个待合并链表的头部。
    • 遍历这两个链表,直到其中一个链表被完全遍历(即其指针为null)。
    • 在每次迭代中,比较list1list2当前指向的节点的值:
      • 如果list1的值较小或等于list2的值(由于题目中可能有重复元素,所以这里使用了小于等于),则将list1的当前节点连接到新链表的末尾(即哨兵节点的next指向的节点之后),并将list1的指针向后移动一位。
      • 否则,将list2的当前节点连接到新链表的末尾,并将list2的指针向后移动一位。
    • 同时,更新新链表的末尾指针(即current指针),使其指向新添加的节点。
  3. 处理剩余链表
    • 当其中一个链表被完全遍历后,如果另一个链表还有剩余节点(即其指针不为null),则将这个剩余链表直接连接到新链表的末尾。
    • 这是因为剩余的链表已经是有序的,所以可以直接添加而不需要进一步排序或比较。
  4. 返回合并后的链表
    • 遍历结束后,新链表的头部就是哨兵节点的next指针所指向的节点(因为哨兵节点只是为了方便操作而创建的,并不属于实际的合并结果)。
    • 所以,返回哨兵节点的next指针即可得到合并后的有序链表。

Java写法:

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {  public ListNode mergeTwoLists(ListNode list1, ListNode list2) {  // 创建一个哨兵节点,方便处理头节点  ListNode sentinel = new ListNode(0);  ListNode current = sentinel;  while (list1 != null && list2 != null) {  if (list1.val < list2.val) {  current.next = list1;  list1 = list1.next;  } else {  current.next = list2;  list2 = list2.next;  }  current = current.next;  }  // 如果list1或list2还有剩余节点,直接连接到结果链表的末尾  if (list1 != null) {  current.next = list1;  }  if (list2 != null) {  current.next = list2;  }  // 返回哨兵节点的下一个节点,即合并后的链表头节点  return sentinel.next;  }  
}
运行时间以及复杂度

 

C++写法:

/**  * 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) {  // 创建一个哨兵节点  ListNode* sentinel = new ListNode(0);  ListNode* current = sentinel;  // 使用双指针迭代合并两个链表  while (list1 != nullptr && list2 != nullptr) {  if (list1->val < list2->val) {  current->next = list1;  list1 = list1->next;  } else {  current->next = list2;  list2 = list2->next;  }  current = current->next;  }  // 处理剩余链表  if (list1 != nullptr) {  current->next = list1;  }  if (list2 != nullptr) {  current->next = list2;  }  // 返回合并后的链表头节点(哨兵节点的下一个节点)  ListNode* mergedHead = sentinel->next;  // 释放哨兵节点内存(可选,如果后续不再使用哨兵节点)  delete sentinel; // 注意:在实际应用中,可能需要保留哨兵节点以简化链表操作  // 但由于哨兵节点在函数返回后可能无法再被访问,因此通常不会在这里删除它  // 除非你确定合并后的链表将使用新的头节点进行管理,并且哨兵节点不再需要  // 由于通常哨兵节点是为了方便链表操作而引入的,并且会在函数外部进行管理,  // 因此这里我们不会删除哨兵节点,而是直接返回它指向的下一个节点作为合并后的链表头  return mergedHead;  // 注意:上面的delete sentinel;在实际情况下通常是不应该执行的,  // 因为哨兵节点是在函数内部创建的,并且其内存管理应该由调用者负责。  // 在这个例子中,我们直接返回了mergedHead,并没有删除哨兵节点。  }  
};  // 注意:在实际使用中,通常不会删除哨兵节点,因为它只是作为一个辅助节点来帮助我们构建新链表。  
// 调用者应该负责管理哨兵节点(尽管在这个特定的例子中,哨兵节点并没有被直接返回给调用者)。  
// 但是,由于哨兵节点的`next`指针指向了合并后的链表头节点,我们可以安全地返回`mergedHead`而不用担心哨兵节点的内存泄漏。
运行时间以及复杂度

 


总结

说点注意事项吧。

  • 内存管理:在C++中,如果链表节点是动态分配的(如使用new关键字),则需要注意内存泄漏的问题。然而,在这个特定的例子中,我们并没有直接返回哨兵节点,而是返回了哨兵节点的下一个节点作为合并后的链表头。因此,哨兵节点的内存管理应由调用者负责,但在函数内部通常不会删除它,因为哨兵节点只是作为辅助节点使用。
  • 链表头节点:合并后的链表头节点是哨兵节点的下一个节点,它在新链表中是第一个真正的数据节点。
  • 链表遍历:在迭代过程中,我们实际上是在遍历两个链表,并通过比较来构建新的链表。当其中一个链表被完全遍历后,我们只需将另一个链表的剩余部分连接到新链表的末尾即可。
  • 链表结构:合并后的链表仍然保持升序排列,且其节点值来自原始的两个链表。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 运维工程师面试整理-安全常见安全漏洞及修复
  • 【RabbitMQ 项目】服务端:数据管理模块之虚拟机模块
  • XWiki中添加 html 二次编辑失效
  • 4.qml单例模式
  • Windows系统通过部署wsl + Goland进行跨平台开发
  • 劳特巴赫ICD调试器CMM调用烧录框架固件研究之C语言版本
  • Android 中使用高德地图实现根据经纬度信息画出轨迹、设置缩放倍数并定位到轨迹路线的方法
  • 浅谈人工智能之基于HTTP方式调用本地QWen OPenAI接口(Java版)
  • Qt_按钮类控件
  • 今日leetcode 349.两个数组的交集
  • Qt 类型选择器和类选择器的区别
  • C++学习笔记(30)
  • 【网络】传输层协议TCP
  • SpringBoot+thymeleaf竞赛报名系统
  • 解决【WVP服务+ZLMediaKit媒体服务】加入海康摄像头后,能发现设备,播放/点播失败,提示推流超时!
  • CEF与代理
  • CentOS学习笔记 - 12. Nginx搭建Centos7.5远程repo
  • javascript从右向左截取指定位数字符的3种方法
  • Spark in action on Kubernetes - Playground搭建与架构浅析
  • Vue2.0 实现互斥
  • windows下mongoDB的环境配置
  • 读懂package.json -- 依赖管理
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 解析 Webpack中import、require、按需加载的执行过程
  • 开源地图数据可视化库——mapnik
  • 那些年我们用过的显示性能指标
  • 入手阿里云新服务器的部署NODE
  • 使用SAX解析XML
  • 小程序 setData 学问多
  • 新版博客前端前瞻
  • 智能网联汽车信息安全
  • 中文输入法与React文本输入框的问题与解决方案
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • 交换综合实验一
  • 整理一些计算机基础知识!
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #{}和${}的区别是什么 -- java面试
  • #13 yum、编译安装与sed命令的使用
  • #include到底该写在哪
  • (003)SlickEdit Unity的补全
  • (35)远程识别(又称无人机识别)(二)
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (c语言版)滑动窗口 给定一个字符串,只包含字母和数字,按要求找出字符串中的最长(连续)子串的长度
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (二)学习JVM —— 垃圾回收机制
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (附源码)计算机毕业设计ssm电影分享网站
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (算法二)滑动窗口
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)jdk与jre的区别
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • ../depcomp: line 571: exec: g++: not found
  • .jks文件(JAVA KeyStore)