021.合并两个有序链表,递归和遍历

embedded/2024/10/21 7:47:26/

题意

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

难度

简单

标签

链表、排序

示例

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]输入:l1 = [], l2 = []
输出:[]输入:l1 = [], l2 = [0]
输出:[0]
复制代码

分析 1

先来读题,关键词,两个升序链表,合并,一个新的升序链表

既然升序过,那最小的数字肯定是两个链表l1和l2中头节点中的一个。

如果最小的是 l1 的头节点,那么第二小的节点肯定是l2的头节点或者l1.next,只要再比较一下两个的大小就能够得到第二小的节点。

如果最小的是 l2 的头节点,那么第二小的肯定是l1的头节点或者l2.next;

那我们就可以采用递归的方式:

if (l1.val < l2.val) {l1.next = mergeTwoLists(l1.next, l2);return l1;
} else {l2.next = mergeTwoLists(l1, l2.next);return l2;
}

如果 l1 的头节点小于 l2 的头节点,那么 l1 的 next 应该是 l1.next 或者 l2;

如果 l1 的头节点大于 l2 的头节点,那么 l2 的 next 应该是 l1 或者 l2.next;

每次递归调用都会返回当前两个链表头部较小的那个节点作为合并链表的当前节点。

递归的结束条件就是 l1 或者 l2 为空,那么返回另一个链表即可。因为如果其中一个链表为空,就意味着另一个链表已经是当前最终的排序链表了,我们可以直接返回非空链表作为合并后的链表

// 如果 l1 为空,返回 l2
if (l1 == null) {return l2;
}
// 如果 l2 为空,返回 l1
if (l2 == null) {return l1;
}

OK,我们来看完整的题解:

class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {// 如果 l1 为空,返回 l2if (l1 == null) {return l2;}// 如果 l2 为空,返回 l1if (l2 == null) {return l1;}// 选择较小的节点,递归地合并剩余部分if (l1.val < l2.val) {l1.next = mergeTwoLists(l1.next, l2);return l1;} else {l2.next = mergeTwoLists(l1, l2.next);return l2;}}
}

递归的关键在于理解每一步递归调用都在做什么,以及递归何时结束。在这个问题中,每一步递归都在处理两个链表当前的头节点,并通过递归调用来处理剩下的节点。递归使得问题规模逐渐变小,直至达到简单的基本情况(一个链表为空),从而完成整个合并过程。

我们来看题解的效率:

分析 2

递归看起来简单,但对于新手来说,理解起来很痛苦,尤其是递归的调用栈,很深。。。深的脑子一片乱麻。

那我们换一种更直接的思路,直接遍历两个链表,依次比较两个链表的节点,将较小的节点放到新链表中。

然后移动指针,继续比较,直到其中一个链表为空,然后将另一个链表剩余的部分直接连接到新链表的末尾。

class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {// 初始化哨兵节点ListNode sentinel = new ListNode(-1);// 初始化一个指针,最初指向哨兵节点ListNode prev = sentinel;// 当两个链表都不为空时,循环继续while (l1 != null && l2 != null) {if (l1.val <= l2.val) {prev.next = l1;l1 = l1.next;} else {prev.next = l2;l2 = l2.next;}// 移动prev指针prev = prev.next;}// 直接将非空链表剩余部分连接到新链表末尾prev.next = l1 == null ? l2 : l1;// 返回哨兵节点的下一个节点,即合并后链表的头节点return sentinel.next;}
}

/*** 用循环遍历的方式** 直接遍历两个链表,依次比较两个链表的节点,将较小的节点放到新链表中。** 然后移动指针,继续比较,直到其中一个链表为空,然后将另一个链表剩余的部分直接连接到新链表的末尾。* @param l1* @param l2* @return*/public ListNode mergeTwoLists2(ListNode l1 ,ListNode l2){//1初始化哨兵节点ListNode sentinel = new ListNode(-1);//2初始化一个指针,最初指向哨兵节点ListNode prev = sentinel;//3当两个链表不为空时,循环遍历两个链表,用prev连接后续节点while (l1 != null&& l2 != null ){//找最小节点开头if (l1.val <=l2.val){//指针 链表的下一个节点为 l1 头节点prev.next = l1;//更新 l1 链表的节点,变成后一个节点l1 = l1.next;}else {prev.next = l2;//后移l2 = l2.next;}//移动prev  。。指向  指针链表的 下一个节点 指向的为 刚才 拼接上的节点prev = prev.next;}//直接将非空链表剩余部分连接到新建表末尾prev.next = l1 ==null ? l2:l1;// 返回哨兵节点的下一个节点,即合并后链表的头节点return sentinel.next;}

这个题解的效率也非常不错,但更容易理解一些:

总结

这道题目是链表的基础题,递归和迭代都可以解决,递归的思路更加简洁,但是对于新手来说,理解起来可能会有些困难,迭代的思路更加直接,但是代码量会多一些。

关键还是理解链表数据结构,这个视频讲的不错,推荐一手。

视频链接:链表 数据结构算法,完整代码动画版,附在线数据结构交互式工具_哔哩哔哩_bilibili

还有这个网站,对链表也进行了详细的讲解,推荐一手。

网站链接:4.2   链表 - Hello 算法

链表另外一个关键点在于理解指针的移动,比如说 prev = prev.next,这个操作不只是移动指针,还相当于删除了 prev 节点,大家可以感受一下。

力扣链接:. - 力扣(LeetCode)


http://www.ppmy.cn/embedded/53793.html

相关文章

【软件工程】【22.04】p1

关键字&#xff1a; 软件需求规约基本性质、数据字典构成、内聚程度最高功能内聚、公有属性、RUP实体类、评审、测试序列、软件确认过程、CMMI能力等级 软件需求分类、DFD数据流图组成&#xff08;实体&#xff09;、经典详细设计、数据耦合、关联多重性、状态图、黑盒测试、…

postman接口工具的详细使用教程

Postman 是一种功能强大的 API 测试工具&#xff0c;可以帮助开发人员和测试人员轻松地发送 HTTP 请求并分析响应。以下是对 Postman 接口测试工具的详细介绍&#xff1a; 安装与设置 安装步骤 访问 Postman 官网&#xff0c;点击右上角的“Download”按钮。 选择你的操作系统…

中介子方程三十四

XXFXXuXXWXXuXXdXXrXXαXXuXpXXKXηXiXXαXXiXηXKXXpXuXXαXXrXXdXXuXWXπXXWXeXyXeXbXπXpXXNXXqXeXXrXXαXXuXpXXKXηXiXXαXXiXηXKXXpXuXXαXXrXXeXqXXNXXpXπXbXeXyXeXWXXπXWXuXXdXXrXXαXXuXpXXKXηXiXXαXXiXηXKXXpXuXXαXXrXXdXXuXXWXXuXXFXXEXXyXXEXXrXXαXXuXpXXK…

iOS APP内存泄漏的问题

iOS APP内存泄漏是指应用程序不再使用内存&#xff0c;但内存却没有被释放&#xff0c;导致应用程序占用过多的内存&#xff0c;甚至崩溃。内存泄漏是iOS开发中常见的问题&#xff0c;会严重影响应用程序的性能和稳定性。北京木奇移动技术有限公司&#xff0c;专业的软件外包开…

动态规划:Leetcode 739. 每日温度

题目描述 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 示例 1: 输入:…

一文了解自定义表单系统开源的多个优势

降本、提质、增效&#xff0c;是当前很多企业都想实现的目的。什么样的软件可以助力企业创造价值&#xff1f;低代码技术平台是近些年得到了很多客户喜爱的平台产品&#xff0c;因为它能帮助大家减少编程代码的撰写&#xff0c;能轻松助力各部门之间做好协调沟通工作&#xff0…

最佳websocket封装

封装了weboskect&#xff0c;完美支持了断网重连、自动心跳的功能&#xff0c;且完全兼容原生写法&#xff0c;无任何学习负担&#xff0c;开开箱即用&#xff01; import { EventDispatcher } from ./dispatcher;export class WebSocketClient extends EventDispatcher {// #…

二叉树 | Java | LeetCode 235 701 450 做题总结,BST特性、 调整二叉树结构(增+删)

235. 二叉搜索树的最近公共祖先 思路&#xff1a;要利用二叉搜索数的性质。当前遍历节点 cur 的数值大于p q时&#xff0c;说明 p q 的父节点在 cur 的左子树。当前遍历节点 cur 的数值小于p q时&#xff0c;说明 p q 的父节点在 cur 的右子树。当前遍历节点 cur 的数值在 p q…