【Day 18】21.合并两个有序链表 2.两数相加

文章目录

  • 21.合并两个有序链表
  • 题目:
  • 思路:迭代
  • 代码实现(Go):
  • 2.两数相加
  • 题目:
  • 思路:
  • 代码实现(Go):

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


思路:迭代

  1. 创建一个虚拟头节点(dummy)

    • 方便操作链表,最终返回 dummy.Next
  2. 定义一个指针 current 指向 dummy

    • 用来构建新的链表。
  3. 遍历两个链表 l1 和 l2

    • 比较 l1 和 l2 当前节点的值:

      • 如果 l1 的值小,就将 l1 的节点接到 current.Next,然后 l1 向后移动。
      • 如果 l2 的值小,就将 l2 的节点接到 current.Next,然后 l2 向后移动。
    • current 每次都向后移动。

  4. 处理剩余节点

    • 当其中一个链表遍历完后,另一个链表可能还有剩余节点,直接接到 current.Next
  5. 返回结果

    • 返回 dummy.Next,跳过虚拟头节点。

时间复杂度:O(n + m)
空间复杂度:O(1)(除了返回的新链表节点,没有额外空间开销)


代码实现(Go):

详细注解:

// package main// import "fmt"// type ListNode struct {
// 	Val  int
// 	Next *ListNode
// }func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {// 创建一个虚拟头节点dummy := &ListNode{} // 已分配了一个空节点,可直接使用cur := dummy// 遍历两个链表,比较节点值for l1 != nil && l2 != nil {if l1.Val < l2.Val {cur.Next = l1l1 = l1.Next} else {cur.Next = l2l2 = l2.Next}cur = cur.Next}// 将剩余节点接上if l1 != nil {cur.Next = l1} else {cur.Next = l2}// 返回合并后的链表(跳过虚拟头节点)return dummy.Next
}// func main() {
// 	// 创建链表 l1: 1->2->4
// 	l1 := &ListNode{
// 		Val: 1,
// 		Next: &ListNode{
// 			Val: 2,
// 			Next: &ListNode{
// 				Val:  4,
// 				Next: nil,
// 			},
// 		},
// 	}// 	// 创建链表 l2: 1->3->4
// 	l2 := &ListNode{
// 		Val: 1,
// 		Next: &ListNode{
// 			Val: 3,
// 			Next: &ListNode{
// 				Val:  4,
// 				Next: nil,
// 			},
// 		},
// 	}// 	merged := mergeTwoLists(l1, l2)// 	// 输出 1->1->2->3->4->4
// 	for merged != nil {
// 		fmt.Print(merged.Val)
// 		if merged.Next != nil {
// 			fmt.Print("->")
// 		}
// 		merged = merged.Next
// 	}
// }

无注释:

package mainimport "fmt"type ListNode struct {Val  intNext *ListNode
}func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode {dummy := &ListNode{}cur := dummyfor l1 != nil && l2 != nil {if l1.Val < l2.Val {cur.Next = l1l1 = l1.Next} else {cur.Next = l2l2 = l2.Next}cur = cur.Next}if l1 != nil {cur.Next = l1} else {cur.Next = l2}return dummy.Next
}func main() {l1 := &ListNode{Val: 1,Next: &ListNode{Val: 2,Next: &ListNode{Val:  4,Next: nil,},},}l2 := &ListNode{Val: 1,Next: &ListNode{Val: 3,Next: &ListNode{Val:  4,Next: nil,},},}merged := mergeTwoLists(l1, l2)for merged != nil {fmt.Print(merged.Val)if merged.Next != nil {fmt.Print("->")}merged = merged.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. 逐位相加

    • 因为链表存储的数字是逆序的,所以直接从头节点开始相加就行。
    • 每次相加 l1.Val + l2.Val + carry,得到 sum
  2. 计算进位

    • 当前节点的值是 sum % 10
    • 进位值是 sum / 10(Go 里用整除)。
  3. 指针后移

    • l1l2 向后移动,如果某个链表已经走到 nil,就按 0 处理。
  4. 处理最后的进位

    • 如果两个链表都结束了,但还有 carry > 0,要新建一个节点存储进位。
  5. 返回结果

    • 同样用一个虚拟头节点(dummy)简化逻辑,最后返回 dummy.Next

在第一次循环时,我们无法往一个空节点的末尾添加节点。这里的技巧是,创建一个虚拟头节点(dummy node),当成初始的「空链表」。循环结束后,虚拟头节点的下一个节点就是最终要返回的链表头节点。相当于占位头。

  • 时间复杂度:O(max(n, m)) 遍历两个链表的长度。
  • 空间复杂度:O(1) 只用常量级变量(不计结果链表本身)。

代码实现(Go):

详细注解:

// package main// import "fmt"// type ListNode struct {
// 	Val  int
// 	Next *ListNode
// }func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {dummy := &ListNode{} // 虚拟头节点cur := dummycarry := 0 // 进位for l1 != nil || l2 != nil || carry != 0 { // 有一个不是空节点,或者还有进位,就继续迭代if l1 != nil {carry += l1.Val // 节点值和进位加在一起l1 = l1.Next    // 下一个节点}if l2 != nil {carry += l2.Val // 节点值和进位加在一起l2 = l2.Next    // 下一个节点}cur.Next = &ListNode{Val: carry % 10} // 当前位carry /= 10                           // 新的进位cur = cur.Next                        // 下一个节点}return dummy.Next
}
// 当 l1 或 l2 为 nil 时,就不会执行 carry += l1.Val 或 carry += l2.Val
// 也就是说,相当于对空链表这一位加 0,不会影响总和// func main() {
// 	// l1 = [2,4,3]
// 	l1 := &ListNode{
// 		Val: 2,
// 		Next: &ListNode{
// 			Val: 4,
// 			Next: &ListNode{
// 				Val:  3,
// 				Next: nil,
// 			},
// 		},
// 	}// 	// l2 = [5,6,4]
// 	l2 := &ListNode{
// 		Val: 5,
// 		Next: &ListNode{
// 			Val: 6,
// 			Next: &ListNode{
// 				Val:  4,
// 				Next: nil,
// 			},
// 		},
// 	}// 	res := addTwoNumbers(l1, l2)// 	// 输出: 7->0->8
// 	for res != nil {
// 		fmt.Print(res.Val)
// 		if res.Next != nil {
// 			fmt.Print("->")
// 		}
// 		res = res.Next
// 	}
// }

无注释:

package mainimport "fmt"type ListNode struct {Val  intNext *ListNode
}func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {dummy := &ListNode{}cur := dummycarry := 0for l1 != nil || l2 != nil || carry != 0 {if l1 != nil {carry += l1.Vall1 = l1.Next}if l2 != nil {carry += l2.Vall2 = l2.Next}cur.Next = &ListNode{Val: carry % 10}carry /= 10cur = cur.Next}return dummy.Next
}func main() {l1 := &ListNode{Val: 2,Next: &ListNode{Val: 4,Next: &ListNode{Val:  3,Next: nil,},},}l2 := &ListNode{Val: 5,Next: &ListNode{Val: 6,Next: &ListNode{Val:  4,Next: nil,},},}res := addTwoNumbers(l1, l2)for res != nil {fmt.Print(res.Val)if res.Next != nil {fmt.Print("->")}res = res.Next}
}

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

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

相关文章

Vue 3 WebSocket通信方案:从原理到实践

Vue 3 WebSocket通信方案&#xff1a;从原理到实践 在现代Web应用开发中&#xff0c;实时通信已成为许多应用的核心需求。从即时聊天到实时数据更新&#xff0c;用户对应用响应速度的期望越来越高。本文将深入剖析一个Vue 3环境下的WebSocket通信方案&#xff0c;包括基础封装与…

Windows 电源管理和 Shutdown 命令详解

一、Windows 电源管理概述 Windows 操作系统通过其内置的电源管理框架&#xff0c;为用户提供了多种电源状态和配置选项&#xff0c;以在性能、能耗和数据安全之间找到最佳平衡点。以下是 Windows 系统中常见的电源状态及其特点&#xff1a; 1. 睡眠&#xff08;Sleep&#xff…

Selenium WebUI 自动化“避坑”指南——从常用 API 到 10 大高频问题

目录 一、为什么 90% 的 UI 自动化脚本活不过 3 个月&#xff1f; 二、Selenium必会 API 速查 三、实践 四、10 大高频异常“症状 → 病因 → 处方” 五、可复用的工具函数 六、面试高频追问&#xff08;附标准答案&#xff09; 一、为什么 90% 的 UI 自动化脚本活不过 …

【微信小程序】微信小程序基于双token的API请求封装与无感刷新实现方案

文章目录前言一、设计思路二、执行流程三、核心模块3.1 全局配置3.2 request封装3.2.1 request方法配置参数3.2.2 请求预处理3.2.3 核心请求流程3.3 刷新accessToken3.4 辅助方法四、api封装示例总结前言 现代前后端分离的模式中&#xff0c;一般都是采用token的方式实现API的…

基于单片机醉酒驾驶检测系统/酒精检测/防疲劳驾驶设计

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览 概述 该设计基于单片机开发&#xff0c;旨在通过实时检测驾驶员酒精浓度&#xff0c;预防酒后驾驶行为…

第6章:垃圾回收分析与调优

1. 垃圾回收基础 1.1 Java 垃圾回收概述 垃圾回收&#xff08;Garbage Collection&#xff0c;GC&#xff09;是 Java 虚拟机自动内存管理的核心机制。理解 GC 的工作原理对于 Java 应用性能调优至关重要。 1.1.1 垃圾回收的目标 自动内存管理&#xff1a;无需手动释放内存防止…

ROS2核心模块-动作通信、参数服务

动作通信 机器人导航到某个目标点,此过程需要一个节点A发布目标信息&#xff0c;然后一个节点B接收到请求并控制移动&#xff0c;最终响应目标达成状态信息。 乍一看&#xff0c;这好像是服务通信实现&#xff0c;因为需求中要A发送目标&#xff0c;B执行并返回结果&#xff0c…

word文档封面中文件编号等标题和内容无法对齐

问题 word文档封面中文件编号等标题和内容无法对齐&#xff0c;因为标题使用的是底纹不是文件内容。 解决办法 字体大小、行距两者配合就可以解决。

163起融资,梅卡曼德融资额夺冠,钉钉、百度智能云10周年,汉桑科技IPO| 2025年8月人工智能投融资观察 · 极新月报

“ 二级的活跃会传导到一级吗&#xff1f;”文&#xff5c;云舒&小鱼编辑 | 小白出品&#xff5c;极新8月重点关注&#xff1a;1、八月人工智能领域投融资事件163起&#xff0c;披露金额76.8亿人民币。2、亿级人民币以上金额的投资事件共20起 。3、八月人工智能领域发生一起…

微信小程序预览和分享文件

预览文档previewFile(val) { let item val.currentTarget.dataset.item wx.downloadFile({url: item.filePath, // 替换为实际的文件地址success: function (res) {let filePath ${wx.env.USER_DATA_PATH}/${item.fileName}|| res.tempFilePath //查看的文件名wx.openDocumen…

开源 C++ QT Widget 开发(十二)图表--环境监测表盘

文章的目的为了记录使用C 进行QT Widget 开发学习的经历。临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 C QT Widget 开发&#xff08;一&#xff09;工程文件结构-CSDN博客 开源…

ARMv8架构01 - ARM64架构寄存器基础

一 、ARM64架构基础 1 ARMv8 A 架构介绍 ARMv8 - A是ARM公司发布的第一代支持64位处理器的指令集和架构。它在扩充64位寄存器的同时提供对上一代架构指令集的兼容&#xff0c;因而能同时提供运行 32位 和 64位应用程序的执行环境。 超大物理地址空间&#xff08;large Physical…

flutter专栏--深入剖析你的第一个flutter应用

使用fvm管理flutter版本 如果你有使用多版本flutter的需求&#xff0c;那么fvm将会给你提供较大的帮助。下面我列举一下mac flutter3.35.2的版本的操作命令&#xff0c;完成之后&#xff0c;你将可以随意切换flutter版本 # 下载fvm相关的依赖 brew tap leoafarias/fvm brew …

MongoDB 聚合查询超时:索引优化与分片策略的踩坑记录

人们眼中的天才之所以卓越非凡&#xff0c;并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。———— 马尔科姆格拉德威尔 &#x1f31f; Hello&#xff0c;我是Xxtaoaooo&#xff01; &#x1f308; “代码是逻辑的诗篇&#xff…

Augmentcode免费额度AI开发WordPress商城实战

Augment AI开发WordPress商城实战&#xff1a;从零构建到免费额度续杯完整指南 前言 在AI编程工具日益普及的今天&#xff0c;如何高效利用这些工具来开发实际项目成为了开发者关注的焦点。本文将详细介绍如何使用Augment AI从零开始构建一个功能完整的WordPress商城系统&#…

【C++八股文】数据结构篇

一、单例模式优化实现 原代码问题分析 ​内存序重排序风险​&#xff1a;双重检查锁在C中可能因指令重排导致半初始化对象被访问​锁粒度过大​&#xff1a;每次获取实例都需要加锁&#xff0c;影响性能​线程安全性不足​&#xff1a;未考虑C11前的内存模型问题 改进方案&a…

并发编程——15 线程池ForkJoinPool实战及其工作原理分析

1 一道算法题引发的思考及其实现 1.1 算法题 问&#xff1a;如何充分利用多核 CPU 的性能&#xff0c;快速对一个2千万大小的数组进行排序&#xff1f; 这道题可以通过归并排序来解决&#xff1b; 1.2 什么是归并排序&#xff1f; 归并排序&#xff08;Merge Sort&#xff…

Kafka面试精讲 Day 6:Kafka日志存储结构与索引机制

【Kafka面试精讲 Day 6】Kafka日志存储结构与索引机制 在“Kafka面试精讲”系列的第6天&#xff0c;我们将深入剖析 Kafka的日志存储结构与索引机制。这是Kafka高性能、高吞吐量背后的核心设计之一&#xff0c;也是中高级面试中的高频考点。面试官常通过这个问题考察候选人是否…

Linux 字符设备驱动框架学习记录(三)

Linux字符设备驱动开发新框架详解 一、新旧驱动框架对比 传统字符设备驱动流程 手动分配设备号 (register_chrdev_region)实现file_operations结构体使用mknod手动创建设备节点 新式驱动框架优势 自动设备号分配&#xff1a;动态申请避免冲突自动节点创建&#xff1a;通过class…

《计算机网络安全》实验报告一 现代网络安全挑战 拒绝服务与分布式拒绝服务攻击的演变与防御策略(1)

目 录 摘 要 一、研究背景与目的 1.1 介绍拒绝服务&#xff08;DoS&#xff09;和分布式拒绝服务&#xff08;DDoS&#xff09;攻击的背景 &#xff08;1&#xff09;拒绝服务攻击&#xff08;DoS&#xff09;  &#xff08;2&#xff09;分布式拒绝服务攻击&#xff0…