每日一题

  • 21. 合并两个有序链表
    • 题目
    • 总体思路
      • 算法步骤
      • 时间复杂度与空间复杂度
    • 代码
  • 2. 两数相加
    • 题目
    • 总体思路
    • 算法步骤
    • 时间复杂度与空间复杂度
    • 代码
    • 知识
    • 感悟

2025.8.30

21. 合并两个有序链表

题目

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

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

总体思路

这个算法采用迭代合并的方式将两个有序链表合并成一个新的有序链表。核心思想是使用一个哑节点(dummy node)作为新链表的起始点,然后逐个比较两个链表的节点值,选择较小的节点连接到新链表中,最后将剩余的节点直接连接到最后。

算法步骤

  1. 初始化:创建哑节点和尾指针
  2. 比较合并:同时遍历两个链表,选择较小值的节点连接
  3. 处理剩余:将未遍历完的链表直接连接到尾部
  4. 返回结果:返回哑节点的下一个节点

时间复杂度与空间复杂度

  • 时间复杂度:O(n + m)
    • 需要遍历两个链表的所有节点
    • n 和 m 分别是两个链表的长度
    • 最坏情况下需要比较 n + m 次
  • 空间复杂度:O(1)
    • 只使用了常数级别的额外空间(哑节点和几个指针)
    • 原地操作,不创建新节点,直接重用原有节点

代码

golang

func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {dump := &ListNode{}tail := dumpfor list1 != nil && list2 != nil {if list1.Val >= list2.Val {tail.Next = list2list2 = list2.Next}else {tail.Next = list1list1 = list1.Next}tail = tail.Next}if list1 != nil {tail.Next = list1}else {tail.Next = list2}return dump.Next
}
/*** 合并两个有序链表* 将两个升序链表合并为一个新的升序链表并返回* * @param list1 *ListNode 第一个有序链表的头节点* @param list2 *ListNode 第二个有序链表的头节点* @return *ListNode 合并后的新链表的头节点*/
func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode {// 创建哑节点(dummy node)作为新链表的起始前驱节点// 使用哑节点可以简化代码,避免处理头节点的特殊情况dummy := &ListNode{}// 尾指针,指向新链表的最后一个节点// 初始时指向哑节点,随着节点的添加而移动tail := dummy// 同时遍历两个链表,直到其中一个链表遍历完毕// 比较两个链表当前节点的值,选择较小的节点添加到新链表for list1 != nil && list2 != nil {if list1.Val <= list2.Val {// 如果list1的当前节点值小于等于list2的当前节点值// 将list1的当前节点连接到新链表的尾部tail.Next = list1// list1指针前进到下一个节点list1 = list1.Next} else {// 如果list2的当前节点值小于list1的当前节点值// 将list2的当前节点连接到新链表的尾部tail.Next = list2// list2指针前进到下一个节点list2 = list2.Next}// 尾指针前进到新添加的节点,保持指向新链表的最后一个节点tail = tail.Next}// 处理剩余的节点:将未遍历完的链表直接连接到新链表的尾部// 由于两个链表都是有序的,剩余部分本身已经是有序的if list1 != nil {// 如果list1还有剩余节点,直接全部连接tail.Next = list1} else {// 如果list2还有剩余节点,或者两个链表都为空,连接list2// 如果两个链表都为空,list2为nil,连接nil也是正确的tail.Next = list2}// 返回哑节点的下一个节点,即新链表的实际头节点// 哑节点本身不存储有效数据,只是辅助节点return dummy.Next
}

2. 两数相加

题目

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:
在这里插入图片描述
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

示例 2:

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

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:

每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零

总体思路

这个算法模拟了手工竖式加法的过程,将两个用链表逆序存储的数字相加。数字的低位存储在链表的前面,高位存储在后面,这样可以直接从头开始逐位相加,正确处理进位问题。

算法步骤

  1. 初始化:创建哑节点、尾指针和进位变量
  2. 逐位相加:同时遍历两个链表,对应位相加加上进位
  3. 处理进位:计算当前位的值和新的进位
  4. 处理剩余:处理链表长度不一致和最终进位的情况
  5. 返回结果:返回结果链表的头节点

时间复杂度与空间复杂度

  • 时间复杂度:O(max(n, m))
    • 需要遍历较长的链表
    • n 和 m 分别是两个链表的长度
    • 每个节点处理时间是常数时间
  • 空间复杂度:O(max(n, m))
    • 需要创建新的链表存储结果
    • 最坏情况下结果链表长度为 max(n, m) + 1(有进位时)
    • 只存储结果,不存储中间过程

代码

golang

/*** Definition for singly-linked list.* type ListNode struct {*     Val int*     Next *ListNode* }*/
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {dump := &ListNode{}tail := dumpcarry := 0for l1 != nil || l2 != nil || carry != 0 {val1, val2 := 0, 0if l1 != nil {val1 = l1.Vall1 = l1.Next}if l2 != nil {val2 = l2.Vall2 = l2.Next}sum := val1 + val2 + carryval := sum % 10carry = sum / 10tail.Next = &ListNode{Val: val}tail = tail.Next}return dump.Next
}
/*** 两个逆序链表表示的数字相加* 数字的低位存储在链表前面,高位存储在后面* 例如:数字 342 表示为 2 -> 4 -> 3* * @param l1 *ListNode 第一个数字的链表表示(逆序)* @param l2 *ListNode 第二个数字的链表表示(逆序) * @return *ListNode 相加结果的链表表示(逆序)*/
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {// 创建哑节点(dummy node)作为结果链表的起始前驱节点// 使用哑节点可以简化代码,避免处理头节点的特殊情况dummy := &ListNode{}// 尾指针,指向结果链表的最后一个节点// 用于高效地在链表尾部添加新节点tail := dummy// 进位值,表示上一位相加产生的进位// 初始为0,因为第一位相加没有前一位的进位carry := 0// 循环条件:只要任一链表还有节点,或者还有进位需要处理,就继续循环// l1 != nil: 第一个链表还有数字位// l2 != nil: 第二个链表还有数字位  // carry != 0: 还有进位需要处理(重要!处理最后一位的进位)for l1 != nil || l2 != nil || carry != 0 {// 初始化当前位的值,如果对应链表为空则值为0val1, val2 := 0, 0// 处理第一个链表的当前位if l1 != nil {val1 = l1.Val    // 获取当前位的值l1 = l1.Next     // 指针移动到下一位}// 处理第二个链表的当前位if l2 != nil {val2 = l2.Val    // 获取当前位的值l2 = l2.Next     // 指针移动到下一位}// 计算当前位的总和:两个数字位加上进位sum := val1 + val2 + carry// 计算当前位的值:取总和除以10的余数// 例如:7 % 10 = 7, 12 % 10 = 2newVal := sum % 10// 计算新的进位值:取总和除以10的商// 例如:7 / 10 = 0, 12 / 10 = 1carry = sum / 10// 创建新节点存储当前位的值,并连接到结果链表尾部tail.Next = &ListNode{Val: newVal}// 移动尾指针到新添加的节点,保持指向链表尾部tail = tail.Next}// 返回哑节点的下一个节点,即结果链表的实际头节点// 哑节点本身不存储有效数据,只是辅助节点return dummy.Next
}

知识

各种结构体初始化方式

package mainimport "fmt"type ListNode struct {Val  intNext *ListNode
}func main() {// 方式1:字段名初始化(最常用)node1 := &ListNode{Val:  1,Next: nil,}fmt.Printf("节点1: Val=%d, Next=%v\n", node1.Val, node1.Next)// 方式2:顺序初始化node2 := &ListNode{2, nil}fmt.Printf("节点2: Val=%d, Next=%v\n", node2.Val, node2.Next)// 方式3:创建结构体后取地址node3 := ListNode{Val: 3}node3Ptr := &node3fmt.Printf("节点3: Val=%d, Next=%v\n", node3Ptr.Val, node3Ptr.Next)// 方式4:使用new函数node4 := new(ListNode)node4.Val = 4fmt.Printf("节点4: Val=%d, Next=%v\n", node4.Val, node4.Next)// 在链表操作中的实际使用dummy := &ListNode{}tail := dummy// 这就是你在代码中看到的语法tail.Next = &ListNode{Val: 5}  // 创建新节点并连接tail = tail.Nexttail.Next = &ListNode{Val: 6}  // 继续添加节点tail = tail.Nextfmt.Print("链表: ")current := dummy.Nextfor current != nil {fmt.Printf("%d", current.Val)if current.Next != nil {fmt.Print(" -> ")}current = current.Next}
}

感悟

最近老是把0和nil写混。
写的时候千万要注意啊!!!

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DVWA靶场通关笔记-文件包含(Impossible级别)

目录 一、源码分析 二、文件包含防范分析 1、明确指定允许包含的文件 2、拒绝所有未在白名单中的输入 3、总结 &#xff08;1&#xff09;白名单 (Allow List) &#xff08;2&#xff09;硬编码/映射 (Hardcoding/Mapping) &#xff08;3&#xff09;输入过滤 (Input F…

构建坚不可摧的数据堡垒:深入解析 Oracle 高可用与容灾技术体系

在当今数字化时代&#xff0c;数据是企业的核心资产&#xff0c;而承载这些数据的数据库系统的连续性与稳定性直接关系到企业的生死存亡。一次计划外的停机或灾难性的数据丢失&#xff0c;带来的不仅是经济上的巨大损失&#xff0c;更是对品牌信誉和客户信任的致命打击。因此&a…

【3D算法技术入门】如何基于建筑图片重建三维数字资产?

要基于建筑图片重建三维数字资产是一个复杂的计算机视觉任务&#xff0c;涉及图像采集、特征提取、相机姿态估计、稠密重建和三维模型优化等多个步骤。下面我将提供一个基于Python的解决方案框架&#xff0c;使用开源库实现从图片到三维模型的基本流程。 首先需要安装必要的库&…

⭐CVPR2025 自动驾驶半监督 LiDAR 分割新范式:HiLoTs 框架深度解析

&#x1f4c4;论文题目&#xff1a;HiLoTs: High-Low Temporal Sensitive Representation Learning for Semi-Supervised LiDAR Segmentation in Autonomous Driving ✍️作者及机构&#xff1a; R.D. Lin、Pengcheng Weng、Yinqiao Wang、Fei Wang&#xff08;西安交通大学软件…

【 MYSQL | 基础篇 函数与约束 】

摘要&#xff1a;本文介绍数据库中的函数与约束&#xff0c;函数含字符串、数值、日期、流程四类&#xff0c;可实现字符串处理、数值计算等需求。约束分六类&#xff0c;重点讲外键约束的语法、删除更新行为&#xff0c;保证数据正确完整。思维导图1. 函数函数是指一段可以直接…

Oracle 数据库性能调优:从瓶颈诊断到精准优化之道

引言&#xff1a;性能优化的本质在当今数据驱动的时代&#xff0c;数据库性能直接关系到企业的运营效率和用户体验。Oracle 作为全球领先的关系型数据库管理系统&#xff0c;承载着众多企业的核心业务。然而&#xff0c;随着数据量的增长和业务复杂度的提升&#xff0c;数据库性…

杨校老师竞赛课堂之C++语言GESP一级笔记

考试大纲 GESP一级考试大纲 计算机基础与编程环境 计算机历史 变量的定义与使用 基本数据类型&#xff08;整型、浮点型、字符型、布尔型&#xff09; 输入与输出&#xff08;cin与cout、scanf与printf&#xff09; 基本运算&#xff08;算术运算、关系运算、逻辑运算&am…

操作系统-管程

1. 为什么需要管程&#xff1f;—— 信号量 (Semaphore) 的困境在理解管程之前&#xff0c;你必须先知道它要解决什么问题。之前&#xff0c;我们使用信号量 (Semaphore) 来实现进程/线程间的同步与互斥。虽然信号量功能强大&#xff0c;但它存在两个主要问题&#xff1a;编程复…

日志的实现

目录 日志与策略模式 Log.hpp class LogStrategy基类 class ConsoleLogStrategy派生类 classFileLogStrategy派生类 日志等级 获得时间戳 localtime_r函数详解 函数原型 struct tm结构的指针 Logger类(重点) class LogMessage 日志信息类 std::stringstream 用法 重…

【论文阅读】Sparse4D v2:Recurrent Temporal Fusion with Sparse Model

标题&#xff1a; Sparse4D v2&#xff1a;Recurrent Temporal Fusion with Sparse Model 作者&#xff1a; Xuewu Lin, Tianwei Lin, Zixiang Pei, Lichao Huang, Zhizhong Su motivation 在v1的基础上&#xff0c;作者发现长时序有更好的效果&#xff0c;但v1的计算量太大&am…

构建免费的音视频转文字工具:支持多语言的语音识别项目

在当今数字时代&#xff0c;音视频内容越来越多&#xff0c;但如何快速将其转换为文字一直是一个挑战。本项目提供了一个免费的解决方案&#xff0c;支持将视频和音频文件转换为文字&#xff0c;并且支持多语言识别。 一个支持中英文的音视频转文字工具&#xff0c;集成了 Vos…

【开题答辩全过程】以 基于SpringBootVue的智能敬老院管理系统为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

Linux 830 shell:expect,ss -ant ,while IFS=read -r line,

[rootsamba caozx26]# scp /home/caozx26/pub root192.168.235.3:~/ root192.168.235.3s password: /home/caozx26/pub: not a regular file [rootsamba caozx26]# ls app km nntp.sh ntp.sh until1.sh 公共 图片 音乐 find.sh l2 ntp1.sh pub u…

​​​​​​​GPT-5发布引爆争议,奥特曼连夜回应!付费充值的Plus用户成最大赢家?

摘要&#xff1a; GPT-5发布后&#xff0c;社区口碑两极分化&#xff0c;从“强无敌”到“还我4o”的呼声并存。面对技术故障和用户质疑&#xff0c;OpenAI CEO萨姆奥尔特曼及团队火速回应&#xff0c;公布了一系列补救措施和未来计划。本文将带你速览这场风波始末&#xff0c;…

Python 操作 Redis 的客户端 - Redis Stream

Python 操作 Redis 的客户端 - Redis Stream1. Redis Stream2. Redis Commands2.1. CoreCommands.xadd() (生产端)2.2. CoreCommands.xlen() (生产端)2.3. CoreCommands.xdel() (生产端)2.4. CoreCommands.xrange() (生产端)2.5. RedisClusterCommands.delete()3. Redis Stream…

【Qt开发】按钮类控件(一)-> QPushButton

目录 1 -> 什么是 PushButton&#xff1f; 2 -> 相关属性 3 -> 代码示例 3.1 -> 带有图标的按钮 3.2 -> 带有快捷键的按钮 4 -> 总结 1 -> 什么是 PushButton&#xff1f; 在 Qt 框架中&#xff0c;QPushButton 是最基础且最常用的按钮控件之一&am…

Citrix 零日漏洞自五月起遭积极利用

安全研究员 Kevin Beaumont 披露了有关 CVE-2025-6543 的惊人细节&#xff0c;这是一个严重的 Citrix NetScaler 漏洞&#xff0c;在该公司发布补丁之前的几个月里&#xff0c;该漏洞被积极利用作为零日攻击。 Citrix 最初将其轻描淡写为简单的“拒绝服务”漏洞&#xff0c;但…

【系列08】端侧AI:构建与部署高效的本地化AI模型 第7章:架构设计与高效算子

第7章&#xff1a;架构设计与高效算子 要将AI模型成功部署到端侧&#xff0c;除了对现有模型进行压缩和优化&#xff0c;更根本的方法是在设计之初就考虑其在资源受限环境下的运行效率。本章将深入探讨如何设计高效的网络架构&#xff0c;以及如何理解并优化常用的核心算子。高…

42-Ansible-Inventory

文章目录Ansible基本概述手动运维时代&#xff08;原始社会&#xff09;自动化运维时代自动化运维工具的优势Ansible的功能及优点Ansible的架构Ansible的执行流程安装AnsibleAnsible配置文件生效顺序Ansible inventory主机清单Ansible基于免秘钥方式管理客户端小结Ansible-Adho…

Go语言runtime/trace工具全面解析

基本概念与功能 Go语言的runtime/trace是Go标准库中内置的性能分析工具,主要用于追踪和可视化Go程序的运行时行为。它能够记录程序执行期间的各种事件,包括goroutine调度、系统调用、垃圾回收(GC)、网络I/O、锁等待等关键信息。 trace工具的核心功能包括: goroutine生命周期…