手写C++ 实现链表的反转、删除、合并
目录
一、手写List成员方法
1.1 打印链表
1.2 删除链表节点
1.3 链表中倒数第k个节点
1.4 反转链表
1.5 合并两个排序链表
二、完整代码
一、C++实现链表成员方法
在上一篇博客《手写链表C++》,实现了基本的List类。在面试中,经常被问到List如何反转、删除元素等,同时也为了丰富List类的成员;这一节本文实现如题等list操作。
C++链表,一种重要的数据结构,由一系列节点构成,每个节点包含两部分:数据和指向下一个节点的指针。链表是一种物理存储单元上非连续、非顺序的存储结构,数据结构的逻辑顺序是通过链表中的指针链接次序实现的。链表的最简单形式是单向链表,它只包含一个信息域和一个指针域。链表的优点是可以动态地分配内存空间,实现高效的数据操作。在C++中,链表的每个节点都是通过指针链接在一起,从而形成一个连续的链式结构。
1.1 打印链表
为了方便debug代码,先写一个打印链表的函数:
void List::print()
{if (size_ == 0){cout << "size = 0" << endl;return;}//遍历Node* p_curr = head_->next_;//【注意这里next】while (p_curr != nullptr){cout << p_curr->data_ << " ";p_curr = p_curr->next_;}cout << endl;
}
1.2 删除链表节点
思想就是找到要删除的Node的前一个节点,让前一个节点的指针指向Node的下一个节点就行了。
例如:pos = 3的时候,for循环执行完毕,p_curr表示索引值为2的节点地址,接着我们让p_curr->next 指向 下一个节点的下一个节点。
//功能:删除索引位置为pos的节点
void List::remove(int pos)
{if (pos < 0 || pos > size_){return;}Node* p_curr = head_;for (int i = 0; i < pos; i++)// 3{p_curr = p_curr->next_;}p_curr->next_ = p_curr->next_->next_;size_--;
}
1.3 链表中倒数第k个节点
你可以去看看剑指offer上的做法,我这里类List维护了一个size_,所以比较简单。
//链表中倒数第k个节点
int List::get_reverse_element(int reverse_pos)
{int pos = size_ - reverse_pos;Node* p_curr = head_;for (int i = 0; i < pos; i++){p_curr = p_curr->next_;}return p_curr->data_;
}
1.4 反转链表
这个方法是必学的,因为面试中经常问,甚至需要现场手撕。
//反转链表
void List::reverse()
{// head -> 1 -> 2 -> 3 -> 4 -> nullptr//nullptr <- 1 <- 2 <- 3 <- 4Node* p_curr = head_->next_;Node* p_prev = nullptr;while (p_curr != nullptr){Node* p_next = p_curr->next_;if (p_next == nullptr){head_->next_ = p_curr;}p_curr->next_ = p_prev;p_prev = p_curr;p_curr = p_next;}
}
下图是反转效果:

1.5 合并两个排序链表
//合并两个排序链表
void mergeLists(List& list3, List& list4, List& list34)
{Node* p_curr3 = list3.head_->next_;Node* p_curr4 = list4.head_->next_;Node* p_curr34 = list34.head_->next_;int location = 0;while ((p_curr3 != nullptr) || (p_curr4 != nullptr)){if ((p_curr3 != nullptr) && (p_curr4 != nullptr)){if (p_curr3->data_ < p_curr4->data_){list34.insert(location, p_curr3->data_);location++;list34.insert(location, p_curr4->data_);location++;}else{list34.insert(location, p_curr4->data_);location++;list34.insert(location, p_curr3->data_);location++;}p_curr3 = p_curr3->next_;p_curr4 = p_curr4->next_;}else if ((p_curr3 != nullptr) && (p_curr4 == nullptr)){list34.insert(location, p_curr3->data_);location++;p_curr3 = p_curr3->next_;}else if ((p_curr3 == nullptr) && (p_curr4 != nullptr)){list34.insert(location, p_curr4->data_);location++;p_curr4 = p_curr4->next_;}}
}
例如现在有两个升序序列:
- A:0 2 4 6 8 14
- B:1 3 5 7 9 12 21 31
要将他们变成一个生序序列;
思路:假设现在两个序列元素个数相等;我们将 0 1对比将得到 0 1, 再将1和2 3 对比 得到 0 1 2 3;再将3和4 5 对比;依次类推,(对应第10行代码)
现在考虑 A序列长度 > B序列长度;对应第29行代码; A序列长度 < B序列长度;对应上述第35行代码。
//合并两个排序链表List list3,list4;for (int i = 0; i < 5; i++){list3.insert(i, 2*i);list4.insert(i, 2 * i + 1);}list3.insert(5, 14);list4.insert(5, 12);list4.insert(6, 21);list4.insert(7, 31);list3.print();list4.print();List list34;mergeLists(list3, list4, list34);list34.print();
测试用例:

二、链表完整代码
以下代码包含:List的实现,节点定义,链表的插入、删除、合并、反转等。
#include<iostream>
using namespace std;class Node
{
public:int data_;//数据阈Node* next_;//指针阈
public:Node() :data_(-1), next_(nullptr) {}
};class List
{
public:List(){this->head_ = new Node();// 不分配空间,下面赋值是不合理的!//this->head_->data_ = 0;//多余?this->head_->next_ = nullptr;this->size_ = 0;};void insert(int pos, int value);void remove(int pos);int get_reverse_element(int reverse_pos);//链表中倒数第k个节点void reverse();int operator[](int i);void print();~List();
public:Node* head_;int size_;//维护一个size
};
//在第pos个元素前一个位置插入(创建、找到位置、入链表)
void List::insert(int pos, int value)
{if (pos < 0 || pos > size_)return;//创建新的节点接受数据Node* newnode = new Node();newnode->data_ = value;//cout << "newnode->data_ = " << *newnode->data_ << endl;newnode->next_ = nullptr;//利用辅助指针找到pos前一个节点// 其实这里不断next,无非就是希望p_curr = nullptr// 然后56行 让newnode->next_ = nullptr(这个nullptr是从head_->next 传过来的);也就是尾部插入嘛// 而循环链表 同理 让newnode->next_ = &(head_)(这个 &(head_) 是从head_->next 传过来的);Node* p_curr = head_;for (int i = 0; i < pos; i++) //这个for循环本质上是head_->next_->next_......{p_curr = p_curr->next_;}//现在p_curr就是pos前一个节点的指针阈//新节点入链表newnode->next_ = p_curr->next_;//右边p_curr->next_ = newnode;//左边size_++;
}void List::remove(int pos)
{if (pos < 0 || pos > size_){return;}Node* p_curr = head_;for (int i = 0; i < pos; i++)// 3{p_curr = p_curr->next_;}p_curr->next_ = p_curr->next_->next_;size_--;
}//链表中倒数第k个节点
int List::get_reverse_element(int reverse_pos)
{int pos = size_ - reverse_pos;Node* p_curr = head_;for (int i = 0; i < pos; i++){p_curr = p_curr->next_;}return p_curr->data_;
}//反转链表
void List::reverse()
{// head -> 1 -> 2 -> 3 -> 4 -> nullptr//nullptr <- 1 <- 2 <- 3 <- 4Node* p_curr = head_->next_;Node* p_prev = nullptr;while (p_curr != nullptr){Node* p_next = p_curr->next_;if (p_next == nullptr)if (p_curr->next_ == nullptr){head_->next_ = p_curr;}p_curr->next_ = p_prev;p_prev = p_curr;p_curr = p_next;}
}int List::operator[](int i)
{Node* p_curr = head_;int count = 0;while (count <= i){p_curr = p_curr->next_;count++;}return p_curr->data_;
}
void List::print()
{if (size_ == 0){cout << "size = 0" << endl;return;}//遍历Node* p_curr = head_->next_;//【注意这里next】while (p_curr != nullptr){cout << p_curr->data_ << " ";p_curr = p_curr->next_;}cout << endl;
}
List::~List()
{while (size_ != 0){Node* p_curr = head_;for (int i = 0; i < (size_ - 1); i++)// 012345 i < 5{p_curr = p_curr->next_;//for循环执行完,p_curr指向4}delete p_curr->next_;//删除最后一个元素p_curr->next_ = nullptr;//末尾元素 空指针size_--;print();}delete head_; //【这个容易忘记!】cout << "delete!" << endl;
}//合并两个排序链表
void mergeLists(List& list3, List& list4, List& list34)
{Node* p_curr3 = list3.head_->next_;Node* p_curr4 = list4.head_->next_;Node* p_curr34 = list34.head_->next_;int location = 0;while ((p_curr3 != nullptr) || (p_curr4 != nullptr)){if ((p_curr3 != nullptr) && (p_curr4 != nullptr)){if (p_curr3->data_ < p_curr4->data_){list34.insert(location, p_curr3->data_);location++;list34.insert(location, p_curr4->data_);location++;}else{list34.insert(location, p_curr4->data_);location++;list34.insert(location, p_curr3->data_);location++;}p_curr3 = p_curr3->next_;p_curr4 = p_curr4->next_;}else if ((p_curr3 != nullptr) && (p_curr4 == nullptr)){list34.insert(location, p_curr3->data_);location++;p_curr3 = p_curr3->next_;}else if ((p_curr3 == nullptr) && (p_curr4 != nullptr)){list34.insert(location, p_curr4->data_);location++;p_curr4 = p_curr4->next_;}}
}int main()
{List list1;//插入for (int i = 0; i < 15; i++){list1.insert(i, i);}//删除list1.remove(10);list1.remove(5);//打印list1.print();list1.reverse();list1.print();//访问倒数元素for (int i = 1; i < 4; i++){cout << "倒数第" << i << "个元素是:" << list1.get_reverse_element(i) << endl;}list1.insert(2, 9999);//重载符[]for (int i = list1.size_ - 1; i >= 0; i--){cout << list1[i] << " ";}cout << endl;List list2;list2.insert(0, 10);list2.insert(1, 20);list2.insert(2, 30);list2.print();int size2 = list2.size_;//合并两个排序链表List list3, list4;for (int i = 0; i < 5; i++){list3.insert(i, 2 * i);list4.insert(i, 2 * i + 1);}list4.insert(5, 12);list4.insert(6, 21);list3.print();list4.print();List list34;mergeLists(list3, list4, list34);list34.print();return 1;
}
相关文章:
手写C++ 实现链表的反转、删除、合并
目录 一、手写List成员方法 1.1 打印链表 1.2 删除链表节点 1.3 链表中倒数第k个节点 1.4 反转链表 1.5 合并两个排序链表 二、完整代码 一、C实现链表成员方法 在上一篇博客《手写链表C》,实现了基本的List类。在面试中,经常被问到List如何反转、…...
虚幻C++基础 day4
虚幻中的UI 虚幻中的比较常用的UI:Widget Blueprint又称UMG虚幻中的两种布局: 网格布局锚布局 创建Widget Blueprint 网格布局 有点类似Qt中的网格布局,将UI面板进行行列切分Horizontal Box:水平分布Vertical Box:…...
【Vue】【uni-app】工单管理页面实现
用的是uni-app的uni-ui拓展组件实现的 功能是对工单进行一个展示,并对工单根据一些筛选条件进行搜索 目前是实现了除了日期之外的搜索功能,测试数据是下面这个tableData.js,都是我自己手写的,后端请求也稍微写了一些,…...
【系统架构设计】架构核心知识: 2.1 软件过程模型
目录 一 软件过程模型 1 瀑布模型 2 V模型 3 喷泉模型 4 增量模型 5 原型模型...
数据管理系统-week1-文件系统、数据库和数据库管理系统
文章目录 前言一、 文件系统文件系统的限制 二、 数据库系统三、 数据库管理系统参考文献 前言 一、 文件系统 对于更高级的数据处理应用程序来说,基于数据块的持久存储逻辑模型过于简单数据块序列被划分为称为文件的数据块的可变子序列,与文件相关的名…...
探索OpenCV中直方图的神奇之处:应用与实现
文章目录 导言:直方图概述:函数原型参数说明:代码示例 应用场景:结语: 导言: 直方图是数字图像处理中一个强大而重要的工具,它通过可视化数据的分布情况,帮助我们更好地理解图像的特…...
MapReduce编程——矩阵乘法(Python版本)
数据格式 对于矩阵元素 A i j A_{ij} Aij,将其处理为 < i , j , M a t r i x N a m e , v a l u e > <i,j,MatrixName,value> <i,j,MatrixName,value>的四元组格式,例如矩阵[[2, 1, 3, 4], [10, -8, 7, 2], [9, 1, 6, -2]]可被转化…...
nature日报:为什么印度德里现在的空气污染如此严重?
为什么印度德里现在的空气污染如此严重? 后季风季节为印度大城市的空气污染积累创造了理想的条件。 本文整理扩展自2023年11月10日nature杂志的NEWS EXPLAINER——Why is Delhi’s air pollution so bad right now? (nature.com) Highlights 季风期间࿰…...
ChatGPT、GPT-4 Turbo接口调用
接口地址 https://chat.xutongbao.top/api/light/chat/createChatCompletion 请求方式 post 请求参数 model可选值: “gpt-3.5-turbo-1106”、 “gpt-3.5-turbo-16k” 、 “gpt-4”、“gpt-4-1106-preview”。 默认值为: “gpt-3.5-turbo-1106” to…...
IDEA中常用的调试快捷键
启动调试 对于Maven项目:Shift F9 对于普通项目:Shift F10 进入调试模式 Shift F9 逐行执行 逐行跳过:F8 逐行步入:F7 逐行步出:Shift F8 继续执行 F9 停止调试 Ctrl F2 设置断点 在代码行号左侧双击&#x…...
需要设计易清洗的口琴
我发现口琴很容易被异物影响。然后就需要清洗。正好手头有一个合适的螺丝刀,还比较方便。 反之一想,应该设计一种口琴,可以方便的拆开,用水清洗。晾干后就能组装。设计上当然会面临一些问题,比如音簧容易变音等。这个可…...
贝锐蒲公英智慧运维方案:实现远程网络监控、管理、维护工业设备
为了提升运维效率,能够及时发现和响应设备的故障、异常和潜在问题。 越来越多的企业都在搭建“集中式”的远程智慧运维体系,以提高运维效率和降低成本。 但是,受限于网络,将不同地域的资源和信息进行整合,实现统一管理…...
Intel oneAPI笔记(4)--jupyter官方文档(Unified Shared Memory)学习笔记
前言 本文是对jupyterlab中oneAPI_Essentials/03_Unified_Shared_Memory文档的学习记录,主要包含对统一共享内存的讲解 USM概述 USM (Unified Shared Memory)是SYCL中基于指针的内存管理。对于使用malloc或new来分配数据的C和C程序员来说应该很熟悉。当将现有的C…...
dRep-基因组质控、去冗余及物种界定
文章目录 Install依赖关系 常用命令常见问题pplacer线程超过30报错当比较基因组很多(>4096)有了Bdv.csv文件后无需输入基因组list 超多基因组为什么需要界定种?dRep重要概念次级ANI的选择Minimum alignment coverage3. 选择有代表性的基因…...
截图贴图软件推荐 - 附下载链接 | Snipaste | Steuna
截图贴图软件推荐 - 附下载链接 | Snipaste | Steuna 前言下载链接Snipaste(推荐)Steuna 前言 Win系统下截图软件多种多样,但贴图软件少之又少,本文介绍2个带有贴图功能的截图软件,分别是Snipaste和Steuna。可将截图固…...
python调用chrome实现网页自动操作
一. 内容简介 python调用chrome实现网页自动操作。 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3代码 链接: 三.主要流程 3.1 下载驱动和插件 调用谷歌浏览器,需要下载浏览器驱动(https://registry.npmmirror.co…...
FFMPEG库实现mp4/flv文件(H264+AAC)的封装与分离
ffmepeg 4.4(亲测可用) 一、使用FFMPEG库封装264视频和acc音频数据到 mp4/flv 文件中 封装流程 1.使用avformat_open_input分别打开视频和音频文件,初始化其AVFormatContext,使用avformat_find_stream_info获取编码器基本信息 2.使…...
《红蓝攻防对抗实战》九.内网穿透之利用GRE协议进行隧道穿透
前文推荐: 《红蓝攻防对抗实战》一. 隧道穿透技术详解 《红蓝攻防对抗实战》二.内网探测协议出网之TCP/UDP协议探测出网 《红蓝攻防对抗实战》三.内网探测协议出网之HTTP/HTTPS协议探测出网 《红蓝攻防对抗实战》四.内网探测协议出网之ICMP协议探测出网 《红蓝…...
大数据毕业设计选题推荐-智慧消防大数据平台-Hadoop-Spark-Hive
✨作者主页:IT毕设梦工厂✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…...
LeetCode 面试题 16.20. T9键盘
文章目录 一、题目二、C# 题解 一、题目 在老式手机上,用户通过数字键盘输入,手机将提供与这些数字相匹配的单词列表。每个数字映射到0至4个字母。给定一个数字序列,实现一个算法来返回匹配单词的列表。你会得到一张含有有效单词的列表。映射…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
安卓基础(Java 和 Gradle 版本)
1. 设置项目的 JDK 版本 方法1:通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分,设置 Gradle JDK 方法2:通过 Settings File → Settings... (或 CtrlAltS)…...
【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...
