【基础算法总结】链表篇
目录
- 一, 链表常用技巧和操作总结
- 二,算法原理和代码实现
- 2.两数相加
- 24.两两交换链表中的节点
- 143.重排链表
- 23.合并k个升序链表
- 25.k个一组翻转链表
- 三,算法总结
一, 链表常用技巧和操作总结
有关链表的算法题也是一类常见并且经典的题型,这些题要使用数据结构中我们手搓的链表结构,也要使用STL中的 list 容器。
下面是几点常用操作的总结:
(1) 善于画图。
(2) 引入虚拟头结点。
方便处理边界情况,就是当没节点第一次头插或尾插的时候的那个判断,引入虚拟头结点就可以避免这个判断(写成 ptail = newHead)。
(3) 不要吝啬空间,大胆定义变量。
(4) 快慢双指针的使用。
主要应用是判环,找链表中环的入口,找链表中倒数第 n 个节点。
(5) 解决链表类的题,一般是:创建一个新节点,尾插操作,头插操作。
我们在学习数据结构时也做过许多有关链表类的经典题目,请浏览:【C/数据结构与算法】10道链表经典OJ。
里面的一些题目与本篇文章的题目也是有关联的,这篇文章算是进阶篇。
二,算法原理和代码实现
2.两数相加
算法原理:
这道题是一道简单的模拟题,模拟两数相加即可。
定义两个指针 cur1和cur2用于遍历两个链表,再定义一个整数 t 用于进位。用 t 和每个节点的值相加,取出 t 的个位创建新节点。
细节问题:
(1) 这里要注意的细节就是链表是逆序的,其实这也是方便我们操作,因为我们可以直接从个位开始相加。
(2) 1. 循环条件:cur1 || cur2 || t
这里要或t是原因:当两个指针都走完时,t里可能还有进位,还需要尾插
(3) 要销毁哨兵位头节点
代码实现:
class Solution
{
public:ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode* cur1 = l1, *cur2 = l2;ListNode* newHead = new ListNode(), *ptail = newHead; // 虚拟头节点int t = 0; // 记录进位while(cur1 || cur2 || t){if(cur1) {t += cur1->val;cur1 = cur1->next;}if(cur2) {t += cur2->val;cur2 = cur2->next;}ListNode* newNode = new ListNode(t % 10);ptail->next = newNode;ptail = newNode;ptail->next = nullptr;t /= 10;}ListNode* pcur = newHead->next;delete newHead;return pcur;}
};
24.两两交换链表中的节点
算法原理:
本题也是一道模拟题,重点是画图,看图写代码!!! 定义四个指针:
根据交换后的链表,进行指针的修改(绿色字),一直循环迭代。
结束条件:
(1) 偶数个节点时:
cur 已经指向空了,next 和 nnext 也无效了,此时结束循环。
(2) 奇数个节点时:
next 已经指向空了, nnext 无效了,此时也结束循环。
代码实现:
class Solution
{
public:ListNode* swapPairs(ListNode* head) {if(head == nullptr) return nullptr;ListNode* newHead = new ListNode(), *prev = newHead;ListNode* cur = head, *next = cur->next;ListNode* nnext = nullptr;if(next) nnext = next->next;prev->next = cur;while(cur && next){// 交换节点prev->next = next;next->next = cur;cur->next = nnext;// 修改指针prev = cur;cur = prev->next;if(cur) next = cur->next;if(next) nnext = next->next;}ListNode* pcur = newHead->next;delete newHead;return pcur;}
};
143.重排链表
算法原理:
这道题比较有综合性,通过分析可以发现:
重排后的链表 = 合并两个链表(原链表的前半段 + 原链表的后半段逆置)。
所以这道题的思路是:
(1) 找到链表的中间节点
(2) 把后半部分逆序
(3) 合并两个链表
代码实现:
class Solution
{
public:// 找中间节点ListNode* FindMiddleNode(ListNode* head){ListNode* slow = head, *fast = head, *prev = head;while(fast && fast->next){prev = slow;slow = slow->next;fast = fast->next->next;}prev->next = nullptr; // 与后半段断开return slow;}// 逆置后半部分链表ListNode* reverseList(ListNode* head){ListNode* phead = head, *ptail = nullptr;while(phead){ListNode* next = phead->next;// 头插法phead->next = ptail;ptail = phead;phead = next;}return ptail;}void reorderList(ListNode* head) {// 处理特殊情况if(head->next == nullptr || head->next->next == nullptr) return;ListNode* mNode = FindMiddleNode(head);ListNode* rNode = reverseList(mNode);// 合并两个链表ListNode* newHead = new ListNode(), *ptail = newHead;ListNode* n1 = head, *n2 = rNode;while(n1 && n2){ptail->next = n1;ptail = ptail->next;n1 = n1->next;ptail->next = n2;ptail = ptail->next;n2 = n2->next;}if(n1) ptail->next = n1;if(n2) ptail->next = n2;delete newHead;}
};
23.合并k个升序链表
算法原理:
解法1:暴力解法,利用"合并两个有序链表"的思想。先把第一个和第二个链表进行合并,得到一个新链表,再用新链表和第三个链表进行合并…。
假设有 k 个链表,每条链表的平均节点有 n 个,则时间复杂度为:n(k-1) + n(k-2) + n(k-3) + … + n --> O(n * k^2)。大概率超时。
解法2:利用优先级队列进行优化。
假设有 k 个链表,建一个大小为 k 的小根堆,把所有链表的第一个节点都扔进小根堆中,堆顶节点就是最小值,取出堆顶节点和虚拟头结点进行链接,再移到下一个节点…
合并链接+找出最小值的节点总的时间复杂度为:O(n * k * logk)。
易错/细节问题:
(1) 优先级队列的第三个模板参数不能直接写成 greater<ListNode * >,因为这样比较的是地址,要写一个仿函数!!
(2) 把链表的所有头节点都入队列时,要注意传递的链表为空的处理。
代码实现:
class Solution
{struct cmp{bool operator()(const ListNode* l1, const ListNode* l2){return l1->val > l2->val;}};
public:ListNode* mergeKLists(vector<ListNode*>& lists){// 建小根堆priority_queue<ListNode*, vector<ListNode*>, cmp> pq;// 把所有头结点进小根堆for (auto l : lists)if (l) pq.push(l);// 合并k个有序链表ListNode* newhead = new ListNode(), * ptail = newhead;while (!pq.empty()){ListNode* pcur = pq.top();pq.pop();ptail->next = pcur;ptail = pcur;if (pcur->next) pq.push(pcur->next);}ListNode* ret = newhead->next;delete newhead;return ret;}
};
解法3:分治-递归
和归并分治的思想一样:
假设有 k 个链表,那么就有 logk 层,递归回退时每个链表的每个节点都执行 logk 次合并,每个链表的平均节点有 n 个,所以时间复杂度为:O(n * k * logk)。
易错/细节问题:
(1) 有两个递归出口:区间不存在和区间里只有一个链表。
(2) 合并两个有序链表时,链表为空的情况。
代码实现:
class Solution
{
public:ListNode* mergeKLists(vector<ListNode*>& lists) {return merge(lists, 0, lists.size()-1);}ListNode* merge(vector<ListNode*>& lists, int left, int right){if(left > right) return nullptr;if(left == right) return lists[left];// 找中点int mid = left + (right - left) / 2;// [left, mid] [mid+1, right]// 合并左右两边ListNode* l1 = merge(lists, left, mid);ListNode* l2 = merge(lists, mid+1, right);// 合并两个有序链表return mergeTwoList(l1, l2);}ListNode* mergeTwoList(ListNode* l1, ListNode* l2){// 处理特殊情况if(l1 == nullptr) return l2;if(l2 == nullptr) return l1;ListNode head; // 把虚拟节点开在栈上head.next = nullptr;ListNode* ptail = &head;while(l1 && l2){if(l1->val < l2->val){ptail->next = l1;ptail = ptail->next;l1 = l1->next;}else{ptail->next = l2;ptail = ptail->next;l2 = l2->next;}}if(l1) ptail->next = l1;if(l2) ptail->next = l2;return head.next;}
};
25.k个一组翻转链表
算法原理:
这也是一道模拟题,算法流程比较简单:
(1) 先求出需要逆序多少组:n。
(2) 重复 n 次,长度为 k 的链表的逆序即可 。
(3) 把不需要逆序的接上。
细节问题:
(1) 这里需要记录第n次头插的开始位置,每次进行新一轮逆序时都要记住该位置。
(2) 如果使用while循环,则每逆序完一组后 k 要重新赋值,不然就一直都是k==0了。
代码实现:
class Solution
{
public:ListNode* reverseKGroup(ListNode* head, int k){// 先求出要逆序多少组int n = 0;ListNode* phead = head;while (phead){phead = phead->next;n++;}n /= k;// 重复 n 次长度为 k 的链表逆序phead = head;ListNode* newhead = new ListNode(), * prev = newhead;for (int i = 0; i < n; i++){ListNode* tmp = phead; // 记录每一组的起点for (int j = 0; j < k; j++){ListNode* next = phead->next;phead->next = prev->next;prev->next = phead;phead = next;}prev = tmp;}// 把不需要翻转的接上prev->next = phead;ListNode* ret = newhead->next;delete newhead;return ret;}
};
三,算法总结
通过上面的若干道题目可以发现,大多数链表类的题目本质上是模拟题,最重要的就是画图,分清各个指针指向哪里。
相关文章:

【基础算法总结】链表篇
目录 一, 链表常用技巧和操作总结二,算法原理和代码实现2.两数相加24.两两交换链表中的节点143.重排链表23.合并k个升序链表25.k个一组翻转链表 三,算法总结 一, 链表常用技巧和操作总结 有关链表的算法题也是一类常见并且经典的题…...
探索路由器静态IP的获取方式
在网络配置中,路由器静态IP是一个重要的概念。对于家庭网络或办公室网络而言,正确配置静态IP地址是确保网络稳定性和管理的关键步骤之一。但是,很多人对于静态IP地址的获取方式可能感到困惑。在本文中,我们将探讨它的获取途径&…...

Vivado - JTAG to AXI Master (GPIO、IIC、HLS_IP)
目录 1. 简介 2. JTAG to AXI Master 2.1 添加 IP Core 2.2 基本TCL命令 2.2.1 复位 JTAG-to-AXI Master 2.2.2 创建并运行写入传输事务 2.2.3 创建并运行读取传输事务 2.2.4 命令列表 2.3 帮助信息 2.4 创建TCL读写程序 2.4.1 Read proc 2.4.2 Write proc 2.4.3 …...
Java中JWT(JSON Web Token)的运用
目录 1. JWT的结构2. JWT的优点3. JWT的流转过程4.具体案例一、项目结构二、依赖配置三、用户模型四、JWT工具类五、JWT请求过滤器六、安全配置七、身份验证控制器八、测试JWT JWT(JSON Web Token)是一种开放标准(RFC 7519)&#…...

CSS3练习--电商web
免责声明:本文仅做分享! 目录 小练--小兔鲜儿 目录构建 SEO 三大标签 Favicon 图标 布局网页 版心 快捷导航(shortcut) 头部(header) logo 导航 搜索 购物车 底部(footer࿰…...

Linux 默认内核版本更改
随笔记录 目录 1. 背景介绍 2. 解决方法 2.1 查看所有可用版本 2.2 安装指定版本内核 2.3 检查当前内核列表 2.4 检查当前默认内核 2.5 设置新的默认内核 2.6 确认内核是否成功加载 2.7 重启 2.8 删除其他版本内核 1. 背景介绍 linux 一般安装多个内核版本&…...

【ubuntu】修改用户名、主机名、主文件夹名、登录名、密码
目录 1.他们是什么 2.修改方法 2.1 修改用户密码 2.2 修改主机名 2.2.1 切换到root用户 2.2.2 修改名称 2.3 修改用户名 主文件夹名 登录名 2.2.1 sudoers 2.2.2 passwd 2.2.3 shadow 2.2.4 group 2.2.5 修改主文件夹名 3.重启 1.他们是什么 (1…...
深入理解JavaScript 的原型继承
JavaScript 的原型链继承机制和 Java 的类继承机制有明显的区别,虽然它们都用于实现对象之间的继承,但它们的实现方式、概念以及运行机制都不同。 1. JavaScript 的原型继承 JavaScript 是基于原型链的继承,主要依赖对象的 __proto__ 属性或…...

Error while loading conda entry point: conda-libmamba-solver
问题 解决方法 conda install --solverclassic conda-forge::conda-libmamba-solver conda-forge::libmamba conda-forge::libmambapy conda-forge::libarchive...
FANUC机器人—PCDK
前言 FANUC提供了一种使用其 PC 开发人员套件 (PCDK) 从 PC 命令和配置机器人的简单方法。该套件允许 PC 访问机器人上的变量、寄存器、IO、程序、位置和警报;接下来,我将如何开始使用 C#。 连接到机器人 将以下突出显示的行添加…...
如何在wsl中使用beyond compare
寫一個名為bc4的文件,內容如下: #!/bin/sh /mnt/c/Program\ Files/Beyond\ Compare\ 4/BComp.com $(wslpath -aw $1) $(wslpath -aw $2)bc4 file1 file2參考:https://forum.scootersoftware.com/forum/beyond-compare-4-discussion/version-…...
CNN+Transformer在自然语言处理中的具体应用
在自然语言处理(NLP)领域,CNN(卷积神经网络)和Transformer架构各自有着广泛的应用。NLP中的具体应用: CNN在NLP中的应用 1.文本分类:CNN可以用于文本分类任务,如情感分析、垃圾邮件…...

DotNetty ChannelRead接收数据为null
问题:C#使用Dotnetty和Java netty服务器通讯,结果能正确发送数据到服务器,却始终接收不到服务器返回的数据。 解决:一定一定要注意服务器和客户端使用的编码一定要完全一样才行 我先前在客户端添加了StringDecoder,服务器却没有…...

3分钟学会下载 blender
1. blender简介 Blender是一款开源的3D创作套件,它由Blender Foundation维护,并得到了全球志愿者和专业开发者的支持。Blender广泛应用于3D模型的制作、动画、渲染、视频编辑、游戏创建、模拟、 composting以及3D打印等多个领域。 功能特点:…...

实现Xshell与虚拟机中Linux服务器的连接(附常见错误解决)
前言 Xshell是一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Microsoft Windows 平台的TELNET 协议。Xshell 通过互联网到远程主机的安全连接以及它创新性的设计和特色帮助用户在复杂的网络环境中享受他们的工作。 本文将介绍Xshell与虚拟机中Linux服务器连接…...

Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)
文章目录 esp-rs 简介GithubRust 包仓库Rust 教程Wokwi 电子模拟器开发环境Rust 环境esp-rs 环境创建 ESP32C3 项目项目结构编译项目命令运行模拟器ESP32C3 烧录 esp-rs 简介 esp-rs 是一个专注于为 Espressif 系列芯片(如 ESP32、ESP32-S2、ESP32-C3 等࿰…...
项目-坦克大战笔记-墙体销毁以及人机销毁
在子弹撞到墙或者人机身上时会将碰撞到的墙体或者人机销毁 我们需要做到几点 检测子弹碰撞到的墙体或者人机将物体获取到 每帧遍历墙体列表与人机列表,检测被碰撞的墙,创建一个方法返回值为对应类型将被碰撞的物体返回出来 public static gudin wallp…...

硬件设计-利用环路设计优化PLL的输出性能
目录 前言 问题描述 问题分析步骤 杂散源头排查 245.76M 参考相噪: 30.72M VCXO的相噪性能测试如下: 解决方案 前言 LMK04832是TI 新发布的低抖动双环去抖模拟时钟, 其最高输出频率可以到达3250MHz, 输出抖动极低,3200MHz…...

Vue入门-Node.js安装
进入Node.js中文网 点击进入Node.js中文网 或者手动输入网址: https://www.nodejs.com.cn/download.html 点击下载64位安装包: 下载好之后双击进行安装 可选择个性化安装或默认安装 直接点【Next】按钮,此处可根据个人需求…...

OpenCV threhold()函数
OpenCV threhold()函数的主要用途是将灰度图转换为二值图像,实现灰度图的二值化,在机器视觉中使用频度较高,如尺寸量测,物体识别等。其原型如下: 函数参数: src 输入数组(多通道、8 位或 32 位浮点…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
反射获取方法和属性
Java反射获取方法 在Java中,反射(Reflection)是一种强大的机制,允许程序在运行时访问和操作类的内部属性和方法。通过反射,可以动态地创建对象、调用方法、改变属性值,这在很多Java框架中如Spring和Hiberna…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...