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

C++ 算法题中链表的操作技巧总结 链表 模拟 力扣 2. 两数相加 题解 每日一题

文章目录

  • 算法题中链表常用的技巧和操作总结
    • 常用技巧
    • 链表核心操作
  • 题目描述
  • 为什么这道题值得你花几分钟看懂?
  • 题目解析
  • 算法原理
  • 代码实现
    • 繁琐版本代码
    • 简洁版本代码
    • 代码解析
    • 时间复杂度与空间复杂度分析
  • 总结
  • 下题预告

在这里插入图片描述
在这里插入图片描述

算法题中链表常用的技巧和操作总结

常用技巧

1. 画图梳理思路
这不仅是链表问题的核心技巧,更是解决所有算法题的通用思路(遇事不决,画图解决)。链表的节点关系依赖指针串联,抽象的指针移动很容易让我们混淆。动手画出节点、指针以及每一步的操作过程,能快速拆解复杂逻辑,把混乱的思路直观化,帮你精准定位问题关键点。

2. 引入虚拟头节点
算法题中给出的链表大多是无哨兵节点的单链表,操作头节点时需要单独处理空指针、边界节点等特殊情况,容易遗漏细节。引入虚拟头节点(哨兵节点)后,无需单独判断头节点边界,无论是删除、插入还是遍历操作,都能统一逻辑,极大降低代码复杂度,让操作更简洁高效。

3. 合理分配空间,灵活定义节点
解题时不必局限于原链表的节点结构,可根据需求大胆定义新节点或辅助指针。适当的空间开销能简化逻辑,避免在原链表上反复修改导致的指针混乱,尤其在合并、拆分链表等场景中,灵活定义节点能让思路更清晰,代码更易维护。

4. 快慢双指针法
这是链表问题的“万能工具”之一,核心思路是让两个指针以不同速度遍历链表。它能高效解决多种经典问题:判断链表是否有环、找到环形链表的入口节点、寻找链表的中间节点、倒数第n个节点等,用这种方法能将时间复杂度优化至O(n),避免暴力遍历的低效问题。

链表核心操作

算法题对链表的考察,本质上都是围绕以下三个核心操作的组合与延伸:
1. 创建新节点
这是链表操作的基础,根据题目给定的值初始化节点,并将节点的指针域置空(避免野指针)。创建节点时需注意内存分配合理性,确保后续指针操作的安全性。

2. 尾插法
将新节点插入链表尾部,核心是找到当前链表的尾节点,再将尾节点的指针指向新节点。尾插法能保持链表元素的插入顺序,常用于构建有序链表或合并多个链表时的元素拼接。

3. 头插法
将新节点插入链表头部(或虚拟头节点之后),核心是让新节点的指针指向当前头节点,再更新头节点为新节点。头插法操作高效(时间复杂度O(1)),常用于链表反转、逆序构建链表等场景。

题目描述

题目链接:力扣 2. 两数相加

题目描述:
给你两个**非空**的链表,表示两个非负的整数。它们每位数字都是按照**逆序**的方式存储的,并且每个节点只能存储**一位**数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807

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

为什么这道题值得你花几分钟看懂?

这道题是链表操作的“入门必修课”,是理解链表遍历、节点创建和进位处理的经典案例。花上几分钟时间,我们能够收获:

  1. 掌握链表的基本操作范式:包括虚拟头节点的使用、指针移动、新节点创建等核心技巧,这些技巧在几乎所有链表问题中都会用到。

  2. 理解模拟加法的完整流程:从低位到高位的加法运算、进位处理、不同长度数字的对齐方式,这些逻辑可迁移到字符串加法、大数运算等问题中。

  3. 学习代码简化的思维方式:从冗长的分支判断到简洁的统一逻辑,掌握如何通过抽象共性条件来优化代码结构,提升可读性和可维护性。

这道题看似简单,但能让我们建立对链表操作的基本认知又或是回顾链表的操作,为后续解决更复杂的链表问题(如反转、合并、环检测等)打下坚实基础。

题目解析

这道题的核心是模拟我们日常做加法的过程,但有两个关键特点需要注意:

  1. 数字的存储方式:链表中的数字是逆序存储的,例如 342 存储为 2 -> 4 -> 3。这恰好方便我们从低位(个位)开始相加,与加法的运算顺序完全一致。

  2. 进位处理:两个数字相加可能产生进位(例如 6 + 8 = 14,需要向高位进 1),且进位可能持续传递(例如 999 + 1 = 1000)。

因此,解题的基本思路是:

  • 同时遍历两个链表,逐位相加对应节点的值
  • 记录每一步的进位,并参与到下一位的计算中
  • 处理两个链表长度不同的情况
  • 处理最后可能剩余的进位

算法原理

模拟加法过程
我们可以按照以下步骤模拟两数相加的过程:

  1. 初始化:创建一个虚拟头节点 head(简化边界处理),以及用于遍历链表的指针和记录进位的变量。
    在这里插入图片描述

  2. 遍历链表:同时遍历两个输入链表,直到两个链表都遍历完毕且没有剩余进位。

  3. 逐位计算

    • 取出当前节点的值(若链表已遍历完毕,则取值为 0)
    • 计算当前位的总和:当前位之和 = l1的值 + l2的值 + 进位
    • 计算当前位的结果(总和 % 10)和新的进位(总和 / 10
    • 创建新节点存储当前位的结果,并移动指针
  4. 处理剩余进位:若遍历结束后仍有进位,需创建新节点存储。

代码实现

繁琐版本的问题
我在最初的实现过程中将可能会分的情况都分开处理:

  • 两个链表都有节点的情况
  • 只有 l1 有节点的情况
  • 只有 l2 有节点的情况
  • 处理剩余进位的情况

这种方式虽然直观,但存在大量重复代码,且逻辑分散,容易出错。

优化过程
通过观察我们可以发现,各种情况的核心计算逻辑是一致的(都是“当前值 + 进位”),差异仅在于是否从链表中取值。因此可以:

  • 用一个循环条件涵盖所有需要处理的情况(cur1 || cur2 || t
  • 在循环内部统一处理取值逻辑(有节点则取值并移动指针,否则取 0)
  • 统一计算当前位结果和进位,无需分情况判断

这种方式将分散的逻辑集中,大大减少了代码量,同时保持了逻辑的清晰性。

繁琐版本代码

/*** 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* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode* head = new ListNode(-1);  // 虚拟头节点ListNode* cur1 = l1;ListNode* cur2 = l2;ListNode* tmp = head;  // 用于构建结果链表的指针int t = 0;  // 进位// 处理两个链表都有节点的部分while (cur1 != nullptr && cur2 != nullptr) {int sum = cur1->val + cur2->val + t;if (sum >= 10) {t = sum / 10;sum %= 10;} else {t = 0;}tmp->next = new ListNode(sum);tmp = tmp->next;cur1 = cur1->next;cur2 = cur2->next;}// 处理 l1 剩余的节点while (cur1 != nullptr) {int sum = cur1->val + t;if (sum >= 10) {t = sum / 10;sum %= 10;} else {t = 0;}tmp->next = new ListNode(sum);tmp = tmp->next;cur1 = cur1->next;}// 处理 l2 剩余的节点while (cur2 != nullptr) {int sum = cur2->val + t;if (sum >= 10) {t = sum / 10;sum %= 10;} else {t = 0;}tmp->next = new ListNode(sum);tmp = tmp->next;cur2 = cur2->next;}// 处理最后剩余的进位if (t != 0) {tmp->next = new ListNode(t);tmp = tmp->next;}return head->next;}
};

简洁版本代码

/*** 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* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode* head = new ListNode(-1);  // 虚拟头节点ListNode* cur1 = l1;ListNode* cur2 = l2;ListNode* tmp = head;  // 用于构建结果链表的指针int t = 0;  // 进位// 循环条件:只要有一个链表未结束,或仍有进位,就继续处理while (cur1 || cur2 || t) {// 累加当前节点的值(若节点存在)if (cur1) {t += cur1->val;cur1 = cur1->next;  // 移动指针}if (cur2) {t += cur2->val;cur2 = cur2->next;  // 移动指针}// 创建当前位的节点(存储 t 的个位)tmp->next = new ListNode(t % 10);tmp = tmp->next;  // 移动指针// 更新进位(t 的十位)t /= 10;}// 面试时注意释放虚拟头节点的内存ListNode* result = head->next;delete head;return result;}
};

代码解析

  1. 虚拟头节点new ListNode(-1) 创建一个虚拟头节点,避免了处理头节点为空的特殊情况,简化了代码逻辑。

  2. 循环条件while (cur1 || cur2 || t) 确保所有情况都被处理:

    • cur1 不为空:l1 还有未处理的节点
    • cur2 不为空:l2 还有未处理的节点
    • t 不为 0:存在未处理的进位
  3. 取值逻辑

    • 若链表指针不为空,则累加其值并移动指针
    • 若指针为空,则相当于累加 0(不做处理)
  4. 结果计算

    • 当前位的结果为 t % 10(取个位)
    • 新的进位为 t / 10(取十位)
  5. 内存管理

    • 笔试时可以直接返回 head->next,无需释放虚拟头节点
    • 面试时需要手动释放虚拟头节点的内存(delete head),体现对内存管理的重视

时间复杂度与空间复杂度分析

  • 时间复杂度:O(max(m, n)),其中 m 和 n 分别是两个链表的长度。循环执行次数取决于较长的链表长度和可能的进位,最多为 max(m, n) + 1(处理最后一个进位)。

  • 空间复杂度:O(max(m, n)),结果链表的长度最多为 max(m, n) + 1(当有进位时),因此需要额外的 O(max(m, n)) 空间存储结果节点。

总结

  1. 核心思路:模拟加法运算过程,从低位到高位逐位计算,同时处理进位,利用链表逆序存储的特点简化运算顺序。

  2. 关键技巧

    • 使用虚拟头节点简化边界处理
    • 统一循环条件涵盖所有情况(链表未结束或有进位)
    • 抽象共性逻辑,减少分支判断
  3. 面试注意

    • 清晰讲解模拟加法的过程,包括进位处理
    • 说明虚拟头节点的作用
    • 注意内存释放,体现良好的编程习惯

这道题虽然简单,但包含了链表操作的核心技巧,掌握这些技巧对于解决更复杂的链表问题至关重要。

下题预告

接下来,我们将继续深耕 链表操作!下一道题,我们就聚焦经典进阶题 24. 两两交换链表中的节点,不仅帮大家搞定这道题的迭代与递归两种核心解题思路,更会借着这道题,和大家一起深入拆解链表节点交换的关键技巧(比如虚拟头节点的进阶使用、多指针联动、边界条件处理等),进一步强化链表操作的实战能力,为后续解决更复杂的链表重组问题奠基~
感兴趣的朋友记得持续关注,咱们一起攻克链表这块“高频考点”!

Doro 带着小花🌸来啦!🌸奖励🌸看到这里的你!如果这篇内容帮你理清了两数相加的解题逻辑,或是让你对链表操作的代码优化有了更清晰的认识,别忘了点赞支持呀!把它收藏起来,以后复习这类问题时翻出来,就能快速回忆起关键细节~关注这个博主,他会持续更新算法系列内容,有什么疑问随时讨论!

在这里插入图片描述

http://www.dtcms.com/a/569854.html

相关文章:

  • CI/CD 是如何改变软件世界的?
  • 企业级Agent智能体(智能小秘)之MCP服务认证实现
  • 无极商城网站建设什么是网络营销策划书
  • 将地球上的距离转化为经纬度差
  • 华为OD机试双机位A卷 - 叠积木 (C++ Python JAVA JS GO)
  • Windows 2008 如何安装IIS?
  • wordpress後台建站赚钱项目
  • Day57 | 一文详解ThreadLocal
  • 快速判断地图上的点是否在多边形内部
  • 网站文章的作用邵阳市今天新闻
  • C#设计模式 单例模式实现方式
  • 网站是怎么搭建的简单个人博客模板网站
  • 【题解】洛谷 P10083 [GDKOI2024 提高组] 不休陀螺 [思维 + 树状数组 + st 表]
  • C语言字符串操作:手写strlen+常用库函数解析
  • 自己可以创建公司网站吗赣州网站制作培训
  • 百度优化排名软件seo交流
  • 链表相关的算法题(1)
  • 速成网站建设有哪些专业做饰品的网站app
  • 服务器负载过高的多维度诊断与性能瓶颈定位指南
  • 超云发布R2425存储服务器:以全栈自研引领国产存储新方向
  • 网站域名快速备案做网站没有高清图片怎么办
  • 【Python基础】f-string用法
  • 前端高频面试手写题——扁平化数组转树
  • 网站建设合同通用范本免费推广引流怎么做
  • 上海怎么建设网站网站建设网站制作公司
  • Flink 多流转换
  • Redis_5_单线程模型
  • 做简单网站用什么软件有哪些洛阳网站建设设计公司
  • CTF WEB入门 命令执行篇29-49
  • IDEA自定义类注释、方法注释