【算法从零到千】【59-65】链表专题

发布时间:2026/9/8 19:29:38

【算法从零到千】【59-65】链表专题 1. 两数相加2. 两数相加https://leetcode.cn/problems/add-two-numbers/https://leetcode.cn/problems/add-two-numbers/给你两个 非空 的链表表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的并且每个节点只能存储 一位 数字。请你将两个数相加并以相同形式返回一个表示和的链表。你可以假设除了数字 0 之外这两个数都不会以 0 开头写法迭代class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { int carry0,all0; ListNode* pheadnew ListNode(0);ListNode* curphead; while(l1l2) { alll1-vall2-valcarry; if(all10){carry1;all%10;} else{carry0;} cur-nextnew ListNode(all); curcur-next; l1l1-next;l2l2-next;all0; } while(l1||l2) { if(l1){alll1-valcarry; l1l1-next;} if(l2){alll2-valcarry; l2l2-next;} if(all10){carry1;all%10;} else{carry0;} cur-nextnew ListNode(all);all0; curcur-next; } if(carry1) { cur-nextnew ListNode(1); curcur-next; } curphead-next; delete phead; return cur; } };优化写法class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { // 构造哑巴节点 dummy最后返回 dummy.next, 以方便处理新链表的头节点。 ListNode* dummy new ListNode(0); ListNode* node dummy; // node 一直会变化前进 int carrier 0; // 进位 // 只要有没走到头的链表或者进位不为 0 就一直前进。 while (l1 || l2 || carrier) { // 求和考虑可能有链表走到头 int sum (l1 ? l1-gt;val : 0) (l2 ? l2-gt;val : 0) carrier; // 在尾部添加节点 node-gt;next new ListNode(sum % 10); node node-gt;next; // 更新进位并向两个链表尾部前进 carrier sum / 10; if (l1) l1 l1-gt;next; if (l2) l2 l2-gt;next; } ListNode* result dummy-gt;next; // 保存结果链表的头节点 delete dummy; // 释放哑节点的内存 return result; } };2. 删除链表的倒数第N个节点19. 删除链表的倒数第 N 个结点https://leetcode.cn/problems/remove-nth-node-from-end-of-list/https://leetcode.cn/problems/remove-nth-node-from-end-of-list/给你一个链表删除链表的倒数第n个结点并且返回链表的头结点写法一双指针class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* curhead;ListNode* prevnullptr; int size0; while(cur) {size;curcur-next;} curhead;int aimsize-n; while(aim--) { prevcur; curcur-next; } if(curhead) return head-next; if(head-next) prev-nextcur-next; else return nullptr; return head; } };写法二快慢指针class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { // 由于可能会删除链表头部用哨兵节点简化代码 ListNode dummy{0, head}; ListNode* left dummy; ListNode* right dummy; while (n--) { right right-next; // 右指针先向右走 n 步 } while (right-next) { left left-next; right right-next; // 左右指针一起走 } // 左指针的下一个节点就是倒数第 n 个节点 ListNode* nxt left-next; left-next left-next-next; delete nxt; return dummy.next; } };3. 排序链表148. 排序链表https://leetcode.cn/problems/sort-list/https://leetcode.cn/problems/sort-list/给你链表的头结点head请将其按 升序 排列并返回 排序后的链表写法一STLclass Solution { public: ListNode* sortList(ListNode* head) { multimapint,ListNode* hash; ListNode* curhead; while(cur) { hash.insert({cur-val,cur}); curcur-next; } ListNode* pheadnew ListNode(0);curphead; for(auto e:hash) { cur-nexte.second; curcur-next; } cur-nextnullptr; return phead-next; } };写法二归并排序class Solution { public: ListNode* sortList(ListNode* head) { return mergeSort(head); } /** * 对给定的链表进行归并排序 */ ListNode* mergeSort(ListNode* head){ // 如果链表为空或只有一个节点无需排序直接返回 if(!head || !head-gt;next){ return head; } // 获取链表的中间节点分别对左右子链表进行排序 ListNode* mid getMid(head); ListNode* rightSorted mergeSort(mid-gt;next); // 排序右子链表 if(mid)mid-gt;next nullptr; // 断开两段子链表 ListNode* leftSorted mergeSort(head); // 排序左子链表 return mergeTwoLists(leftSorted, rightSorted); // 两个子链表必然有序合并两个有序的链表 } /** * 获取以head为头节点的链表中间节点 * 如果链表长度为奇数返回最中间的那个节点 * 如果链表长度为偶数返回中间靠左的那个节点 */ ListNode* getMid(ListNode* head){ if(!head)return head; ListNode* slow head, *fast head-gt;next; // 快慢指针慢指针初始为 while(fast ! nullptr amp;amp; fast-gt;next ! nullptr) { fast fast-gt;next-gt;next; // 快指针每次移动两个节点 slow slow-gt;next; // 慢指针每次移动一个节点 } return slow; // 快指针到达链表尾部时慢指针即指向中间节点 } /** * 合并两个有序链表list1和list2 */ ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { ListNode* dummy new ListNode(); // 伪头节点用于定位合并链表的头节点 ListNode* node dummy; // 新链表当前的最后一个节点初始为伪头节点 // 直到两个链表都遍历完了合并结束 while(list1 ! nullptr || list2 ! nullptr){ int val1 list1 nullptr ? 50001 : list1 -gt; val; // 如果链表1已经遍历完val1取最大值保证链表2的节点被选择到 int val2 list2 nullptr ? 50001 : list2 -gt; val; // 如果链表2已经遍历完val2取最大值保证链表1的节点被选择到 if(val1 lt; val2){ // 链表1的节点值更小加入到合并链表并更新链表1指向的节点 node -gt; next list1; list1 list1 -gt; next; }else{ // 链表2的节点值更小加入到合并链表并更新链表2指向的节点 node -gt; next list2; list2 list2 -gt; next; } node node -gt; next; // 更新合并链表当前的最后一个节点指向 } return dummy -gt; next; // 伪头节点的下一个节点即为合并链表的头节点 } };4. 重排链表4LCR 026. 重排链表https://leetcode.cn/problems/LGjMqU/https://leetcode.cn/problems/LGjMqU/给定一个单链表L的头节点head单链表L表示为L0 → L1 → … → Ln-1 → Ln请将其重新排列后变为L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …不能只是单纯的改变节点内部的值而是需要实际的进行节点交换写法一STL映射class Solution { public: void reorderList(ListNode* head) { if (!head-next) return; //处理边界 maplt;int,ListNode*gt; index;//记录位置 ListNode* curhead;int i0; while(cur) { index[i]cur; curcur-gt;next; } i--;//保证索引正确 curhead;int j1;//前后挨个插入 while(jlt;i) { cur-gt;nextindex[i--]; curcur-gt;next; if(i!j)//当ij只执行一次 {cur-gt;nextindex[j]; curcur-gt;next;} } cur-gt;nextnullptr; } };写法二反转三指针class Solution { public: void reorderList(ListNode* head) { ListNode* dummmy new ListNode(0); dummmy-next head; ListNode* slow dummmy; ListNode* fast dummmy; while (fast ! nullptr fast-next ! nullptr) { slow slow-next; fast fast-next-next; } delete dummmy; dummmy nullptr; ListNode* headB slow-next; slow-next nullptr; ListNode* p2 reverseList(headB); ListNode* p1 head; ListNode* p3 nullptr; while (p2 ! nullptr) { p3 p1-next; p1-next p2; p1 p2; p2 p3; } } ListNode* reverseList(ListNode* head) { if (head nullptr) { return head; } ListNode* left nullptr; ListNode* cur head; ListNode* right nullptr; while (cur ! nullptr) { right cur-gt;next; cur-gt;next left; left cur; cur right; } return left; } };5. 两两交换链表中的节点24. 两两交换链表中的节点https://leetcode.cn/problems/swap-nodes-in-pairs/https://leetcode.cn/problems/swap-nodes-in-pairs/给你一个链表两两交换其中相邻的节点并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题即只能进行节点交换。写法一迭代class Solution { public: ListNode* swapPairs(ListNode* head) { if(!head||!head-next) return head; ListNode* curhead-next;ListNode* prevhead; //完成前两个 prev-nextcur-next; cur-nexthead;headcur; //后面再规律处理 curprev; prevhead;ListNode* prevvnullptr; while(cur-nextcur-next-next) { //规律移动 if(cur-nextcur-next-next){ prevvcur; curcur-next-next; prevprev-next-next; } //交换流程 prev-nextcur-next; cur-nextprev; prevv-nextcur; //保持cur与prev的前后关系 curprev; prevprevv-next; } //如果还剩下则为一个不用交换 return head; } };写法二递归class Solution { public: ListNode* swapPairs(ListNode* head) { if (head nullptr || head-next nullptr) { return head; } ListNode* node1 head; ListNode* node2 head-gt;next; ListNode* node3 node2-gt;next; node1-gt;next swapPairs(node3); // 1 指向递归返回的链表头 node2-gt;next node1; // 2 指向 1 return node2; // 返回交换后的链表头节点 } };6. 合并K个升序链表23. 合并 K 个升序链表https://leetcode.cn/problems/merge-k-sorted-lists/https://leetcode.cn/problems/merge-k-sorted-lists/给你一个链表数组每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中返回合并后的链表。写法一STL映射class Solution { public: ListNode* mergeKLists(vectorListNode* lists) { if(lists.size()0)return nullptr; if(lists.size()1)return lists[0]; //用哈希记录索引和节点地址 multimapint,ListNode* nodemap; for(auto e:lists) { ListNode* cure; while(cur) { nodemap.insert({cur-val,cur}); curcur-next; } } //重组链表 ListNode* pheadnew ListNode(0);ListNode* curphead; for(auto e:nodemap) { cur-nexte.second; curcur-next; } cur-nextnullptr; return phead-next; } };写法二优先级队列⭐⭐class Solution { public: ListNode* mergeKLists(vectorListNode* lists) { auto cmp [](const ListNode* a, const ListNode* b) { return a-val b-val; // 最小堆 }; priority_queueListNode*, vectorListNode*, decltype(cmp) pq; for (auto head : lists) { if (head) { pq.push(head); // 把所有非空链表的头节点入堆 } } ListNode dummy{}; // 哨兵节点作为合并后链表头节点的前一个节点 auto cur amp;dummy; while (!pq.empty()) { // 循环直到堆为空 auto node pq.top(); // 剩余节点中的最小节点 pq.pop(); if (node-gt;next) { // 下一个节点不为空 pq.push(node-gt;next); // 下一个节点有可能是最小节点入堆 } cur-gt;next node; // 把 node 添加到新链表的末尾 cur cur-gt;next; // 准备合并下一个节点 } return dummy.next; // 哨兵节点的下一个节点就是新链表的头节点 } };非常重要7. K个一组翻转链表25. K 个一组翻转链表https://leetcode.cn/problems/reverse-nodes-in-k-group/https://leetcode.cn/problems/reverse-nodes-in-k-group/给你链表的头节点head每k个节点一组进行翻转请你返回修改后的链表。k是一个正整数它的值小于或等于链表的长度。如果节点总数不是k的整数倍那么请将最后剩余的节点保持原有顺序。你不能只是单纯的改变节点内部的值而是需要实际进行节点交换。写法一栈class Solution { public: ListNode* reverseKGroup(ListNode* head, int k) { if (k 1) return head; stackListNode* st; ListNode* pheadnew ListNode(0);ListNode* pcurphead; ListNode* curhead; //统计节点数量 int size0; while(cur){curcur-next;size;} //按k的数量依次处理 int n0,count0;curhead; while(countksize) { while(nkcur) { st.push(cur); curcur-next; n;count; } while(!st.empty()) { pcur-nextst.top(); st.pop(); pcurpcur-next; } n0; } //处理末尾情况 if(countsize) pcur-nextcur; else pcur-nextnullptr; curphead-gt;next; delete phead; return cur; } };写法二递归class Solution { public: ListNode* reverseKGroup(ListNode* head, int k) { ListNode *p head; for(int i 0; i k; i) { if(!p) return head; p p-next; } ListNode *q head; ListNode *pre nullptr; while(q ! p) { ListNode *tmp q-gt;next; q-gt;next pre; pre q; q tmp; } head-gt;next reverseKGroup(p, k); return pre; } };
延伸阅读

更多相关文章

2026/8/27 22:20:31

RevokeMsgPatcher:微信QQ防撤回补丁怎么用?

RevokeMsgPatcher:微信QQ防撤回补丁怎么用? 【免费下载链接】RevokeMsgPatcher :trollface: A hex editor for WeChat/QQ/TIM - PC版微信/QQ/TIM防撤回补丁(我已经看到了,撤回也没用了) 项目地址: https://gitcode.c…

2026/9/8 8:55:28

3 步免费把 NCM 加密音乐转成 MP3:ncmdump 离线转换入门教程

3 步免费把 NCM 加密音乐转成 MP3:ncmdump 离线转换入门教程 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump ncmdump 是一款完全免费、开源的本地加密音乐转换工具,把网易云 NCM 加密文件转成通用 MP3。全程无…

2026/9/9 3:56:13

opencode 实战:终端 AI Agent 的配置、扩展与排错全指南

这几年终端 AI Agent 的迭代速度,真的比很多人想象中还要夸张。我从 Claude Code 用起,中途换过 Codex CLI,最后长期留在 opencode 上。倒不是因为它名字好记,而是它把“终端 Agent”这个概念做得足够开放:不锁死某一家…

2026/9/9 3:56:13

工控机Ubuntu安装Intel NPU驱动全攻略:从内核到OpenVINO

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/9 3:56:13

ARDEP开源车载开发平台:Zephyr车规级BSP与CAN FD实时通信实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/9 3:56:13

电机热网络温度预测模型:从参数辨识到在线观测的工程实践

做电机台架试验那阵子,我白天测温升、晚上跑仿真,最头疼的事情不是试验设备出故障,而是仿真模型算出来的绕组温度和实测值对不上。有一次稳态工况下仿真给出的绕组热点只有85℃,实际热电偶已经测到了118℃,差了三十多度…

2026/9/9 3:56:13

SpringBoot集成OnlyOffice:实现文档在线预览与编辑的完整指南

1. 选型判断:在线编辑方案那么多,为什么最终落在onlyoffice先交代一下我遇到这个需求的场景。业务方提了一个很常见的要求:要在网页端直接预览和编辑Word、Excel、PPT,并且编辑完要能自动同步回服务器。原话是"就像腾讯文档那…

2026/9/9 3:51:12

基于Tampermonkey的秒杀插件:自定义规则实现任意网站抢购自动化

简介:面向有抢购、秒杀需求的网购用户,这款Chrome浏览器秒杀辅助插件通过自定义定时任务降低手工操作失误率。支持任意网站添加秒杀任务,可视化选择目标按钮或DOM元素,选取时使用鼠标右键即可完成配置;自定义秒杀频率、…

2026/9/8 7:15:10

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/8 7:15:10

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/9 0:00:48

MHS模型硬件标准:让大模型像调用软件一样控制物理设备

让Claude真正看着显微镜说“这个细胞形态不太对”,或者让大模型自己调一版机械臂的运动轨迹,这事儿听上去已经很接近科幻片了。但你真上手试一次就会发现,模型不缺智商,缺的是一个能插进显微镜、机械臂、激光控制器里的“通用插座…

2026/9/9 0:00:48

AI五大核心方向详解:从机器学习到大模型,零基础转行选哪条?

会有人告诉我,他想转行学AI,但打开招聘网站一看直接傻眼:机器学习、深度学习、自然语言处理、计算机视觉、大模型应用……满屏都是这些词,好像每个都会一点,又好像每个都离自己很远。还有人上来就问“学Python还是学Ja…

2026/9/9 0:00:49

从50行最小循环到生产级AI引擎:工程化改造全解析

直接说干货。这一章我写的不是那种"hello world跑通某个模型"的教程,而是把AI引擎当做一个真正要上线、要被人调用、要扛流量的系统来聊。从最初只有50行的最小循环,到能够承载生产流量的AI引擎,中间差的不是代码量,而是…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/7 22:45:59

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码