C++实现链表
C++实现链表
众所周知,C/C++语言实现的链表是由一个一个的结点构成,每个结点分为数据域和指针域,指针域中存储了其后继结点的地址,通过地址来访问下一个结点。
链表是一系列节点串联形成的数据结构,链表存储有序的元素集合,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的部分和一个指向下一个元素的链接部分组成。因此链表增删非首尾元素时不需要移动元素,只需要更改链接部分的值即可。
本文仅以单链表为例介绍。
单链表每个节点的结构如下:
单链表只有一个指向其他节点的变量,也就是在这种类型的数据结构中,任何两个数据元素之间只有一个链接,参见下图:
链表的操作包括了创建、删除、插入、输出等。
创建就是空间的分配,将头、尾指针及链表结点个数等初始化。删除和插入根据被操作元素的位置可以细分为头删除(插入),尾删除(插入),中间删除(插入)。
插入操作
头插入实际上是增加一个新节点,然后把新增加的结点指针指向原来头指针指向的元素,再把头指针指向新增的节点。
尾插入也是增加一个新节点,该节点指针置为null,然后把原尾结点指针指向新增加的节点,最后把尾指针指向新增加的节点即可。
中间插入稍复杂,首先增加一个节点,然后新增节点的指针指向插入位置的后一个节点,把插入位置的前一个节点指针指向新插入节点即可。
删除操作
删除头元素时,先将头指针指向下一个节点,然后把原头结点的指针置空即可。
删除尾元素时,首先找到链表倒数第2个元素,然后把尾指针指向这个元素,接着把原倒数第2个元素的指针置空。
删除中间元素相对复杂一些,首先将要删除的节点的前一个节点指针指向要删除的节点的下一个节点,然后把要删除节点的指针置空。
上面提到是单链表最基本的操作,除此之外还有其它操作不多说了。下面给出代码示例。本文介绍两种实现方法:一、使用指针自定义实现;二、使用C++ STL(标准模板库)中的list容器实现。
一、使用指针自定义实现
先看一个简单点的示例源码:
// 插入
// 将x插入到以head为头节点的pos位置上
void insert(node* head, int pos, int x){node *p=head;for(int i=0;i<pos-1;i++){//比如要插到第三个位置,从0开始的话就是下标为2,前一个下标为1 p=p->next;// 1 < 3-1 }node* q=new node;q->data=x;q->next=p->next;p->next=q;
} //删除
//删除所有值为x的数
void del(node* head, int x){node* p= head->next;node* pre=head;while(p!=NULL){if(p->data==x){pre->next=p->next;delete(p);p=pre->next;}else{pre=p;p=p->next;}}
}//查找
//查找链表中有几个x,返回count值
int search(node *head, int x){int count=0;node *p=head->next;while(p!=NULL){if(p->data==x){count++;}p=p->next; } return count;
} //排序
//冒泡排序
void sort(node *head){for(node *temp=head->next;temp->next!=NULL;temp=temp->next){for(node *p=head->next;p->next!=NULL;p=p->next){if(p->data > p->next->data){int t=p->data;p->data=p->next->data;p->next->data=t;}}}
} int main(){int array[5]={6,3,9,1,2};node* L=create(array); //返回头节点L //insert(L, 3, 8); //插入后结果为6,3,8,9,1,2 //del(L, 9); //删除后结果为6,3,1,2 int y=9; int x=search(L, y);cout<<"查找到"<< x <<"个"<<y<<endl;//sort(L); //排序 //遍历打印 L=L->next; //头节点L是没有数据域的,下个结点才有 while(L != NULL){cout<<L->data<<" ";L=L->next;} return 0;
}
运行输出:
再给出一个较完善的示例,源码如下:
// 单链表.cpp: #include<iostream>
using namespace std;typedef int DataType;
#define Node ElemType//构建一个节点类
class Node
{
public:int data; //数据域Node * next; //指针域
};//构建一个单链表类
class LinkList
{
public:LinkList(); //构建一个单链表;~LinkList(); //销毁一个单链表;void CreateLinkList(int n); //创建一个单链表void TravalLinkList(); //遍历线性表int GetLength(); //获取线性表长度bool IsEmpty(); //判断单链表是否为空ElemType * Find(DataType data); //查找节点void InsertElemAtEnd(DataType data); //在尾部插入指定的元素void InsertElemAtIndex(DataType data,int n); //在指定位置插入指定元素void InsertElemAtHead(DataType data); //在头部插入指定元素void DeleteElemAtEnd(); //在尾部删除元素void DeleteAll(); //删除所有数据void DeleteElemAtPoint(DataType data); //删除指定的数据void DeleteElemAtHead(); //在头部删除节点
private:ElemType * head; //头结点
};//初始化单链表
LinkList::LinkList()
{head = new ElemType; head->data = 0; //将头结点的数据域定义为0head->next = NULL; //头结点的下一个定义为NULL
} //销毁单链表
LinkList::~LinkList()
{delete head; //删除头结点
} //创建一个单链表
void LinkList::CreateLinkList(int n)
{ElemType *pnew, *ptemp;ptemp = head;if (n < 0) { //当输入的值有误时,处理异常cout << "输入的节点个数有误" << endl; }for (int i = 0; i < n;i++) { //将值一个一个插入单链表中pnew = new ElemType;cout << "请输入第" << i + 1 << "个值: " ;cin >> pnew->data;pnew->next = NULL; //新节点的下一个地址为NULLptemp->next = pnew; //当前结点的下一个地址设为新节点ptemp = pnew; //将当前结点设为新节点}
}//遍历单链表
void LinkList::TravalLinkList()
{if (head == NULL || head->next ==NULL) {cout << "链表为空表" << endl;}ElemType *p = head; //另指针指向头结点while (p->next != NULL) //当指针的下一个地址不为空时,循环输出p的数据域{p = p->next; //p指向p的下一个地址cout << p->data << " ";}
}//获取单链表的长度
int LinkList::GetLength()
{int count = 0; //定义count计数ElemType *p = head->next; //定义p指向头结点while (p != NULL) //当指针的下一个地址不为空时,count+1{count++; p = p->next; //p指向p的下一个地址}return count; //返回count的数据
}//判断单链表是否为空
bool LinkList::IsEmpty()
{if (head->next == NULL) { return true;}return false;
}//查找节点
ElemType * LinkList::Find(DataType data)
{ElemType * p = head;if (p == NULL) { //当为空表时,报异常cout << "此链表为空链表" << endl;return NULL;}else{while (p->next != NULL) //循环每一个节点{if (p->data == data) {return p; //返回指针域}p = p->next;}if (p->data == data) { return p; }return NULL; //未查询到结果}
}//在尾部插入指定的元素
void LinkList::InsertElemAtEnd(DataType data)
{ElemType * newNode = new ElemType; //定义一个Node结点指针newNodenewNode->next = NULL; //定义newNode的数据域和指针域newNode->data = data;ElemType * p = head; //定义指针p指向头结点if (head == NULL) { //当头结点为空时,设置newNode为头结点head = newNode;}else //循环知道最后一个节点,将newNode放置在最后{while (p->next != NULL){p = p->next;}p->next = newNode;}
}//在指定位置插入指定元素
void LinkList::InsertElemAtIndex(DataType data,int n)
{if (n<1 || n>GetLength()) //输入有误报异常cout << "输入的值错误" << endl;else{ElemType * ptemp = new ElemType; //创建一个新的节点ptemp->data = data; //定义数据域ElemType * p = head; //创建一个指针指向头结点int i = 1;while (n > i) //遍历到指定的位置{p = p->next;i++;}ptemp->next = p->next; //将新节点插入到指定位置p->next = ptemp;}
}//在头部插入指定元素
void LinkList::InsertElemAtHead(DataType data)
{ElemType * newNode = new ElemType; //定义一个Node结点指针newNodenewNode->data = data;ElemType * p = head; //定义指针p指向头结点if (head == NULL) { //当头结点为空时,设置newNode为头结点head = newNode;}newNode->next = p->next; //将新节点插入到指定位置p->next = newNode;
}//在尾部删除元素
void LinkList::DeleteElemAtEnd()
{ElemType * p = head; //创建一个指针指向头结点ElemType * ptemp = NULL; //创建一个占位节点if (p->next == NULL) { //判断链表是否为空cout << "单链表空" << endl;}else{while (p->next != NULL) //循环到尾部的前一个{ptemp = p; //将ptemp指向尾部的前一个节点p = p->next; //p指向最后一个节点}delete p; //删除尾部节点p = NULL;ptemp->next = NULL;}
}//删除所有数据
void LinkList::DeleteAll()
{ElemType * p = head->next;ElemType * ptemp = new ElemType;while (p != NULL) //在头结点的下一个节点逐个删除节点{ptemp = p;p = p->next;head->next = p;ptemp->next = NULL;delete ptemp;}head->next = NULL; //头结点的下一个节点指向NULL
}//删除指定的数据
void LinkList::DeleteElemAtPoint(DataType data)
{ElemType * ptemp = Find(data); //查找到指定数据的节点位置if (ptemp == head->next) { //判断是不是头结点的下一个节点,如果是就从头部删了它DeleteElemAtHead();}else{ElemType * p = head; //p指向头结点while (p->next != ptemp) //p循环到指定位置的前一个节点{p = p->next;}p->next = ptemp->next; //删除指定位置的节点delete ptemp;ptemp = NULL; }}//在头部删除节点
void LinkList::DeleteElemAtHead()
{ElemType * p = head;if (p == NULL || p->next == NULL) { //判断是否为空表,报异常cout << "该链表为空表" << endl;}else{ElemType * ptemp = NULL; //创建一个占位节点p = p->next;ptemp = p->next; //将头结点的下下个节点指向占位节点delete p; //删除头结点的下一个节点p = NULL;head->next = ptemp; //头结点的指针更换}
}//测试函数
int main()
{LinkList l;int i;cout << "1.创建单链表(整数类型数据) 2.遍历单链表 3.获取单链表的长度 4.判断单链表是否为空 \n";cout << "5.获取节点 6.在尾部插入指定元素 7.在指定位置插入指定元素 8.在头部插入指定元素\n";cout << "9.在尾部删除元素 10.删除所有元素 11.删除指定元素 12.在头部删除元素 0.退出" << endl;cout << " -------- "<< endl;do{cout << "请输入要执行的操作: ";cin >> i;switch (i){case 1:int n;cout << "请输入单链表的长度: ";cin >> n;l.CreateLinkList(n);break;case 2:l.TravalLinkList();break;case 3:cout << "该单链表的长度为" << l.GetLength() << endl;break;case 4:if (l.IsEmpty() == 1)cout << "该单链表是空表" << endl;else{cout << "该单链表不是空表" << endl;}break;case 5:DataType data;cout << "请输入要获取节点的值: ";cin >> data;cout << "该节点的值为" << l.Find(data)->data << endl;break;case 6:DataType endData;cout << "请输入要在尾部插入的值: ";cin >> endData;l.InsertElemAtEnd(endData);break;case 7:DataType pointData;int index;cout << "请输入要插入的数据: ";cin >> pointData;cout << "请输入要插入数据的位置: ";cin >> index;l.InsertElemAtIndex(pointData, index);break;case 8:DataType headData;cout << "请输入要在头部插入的值: ";cin >> headData;l.InsertElemAtHead(headData);break;case 9:l.DeleteElemAtEnd();break;case 10:l.DeleteAll();break;case 11:DataType pointDeleteData;cout << "请输入要删除的数据: ";cin >> pointDeleteData;l.DeleteElemAtPoint(pointDeleteData);break;case 12:l.DeleteElemAtHead();break;default:break;}}while (i != 0);system("pause");return 0;
}
运行结果:
二、使用C++ STL(标准模板库)中的list容器实现
C++ STL(标准模板库)是一套功能强大的 C++ 模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。
STL list 容器,又称双向链表容器,即该容器的底层是以双向链表的形式实现的。
std::list_C++中文网
要用list容器必须声明请头文件:#include <list>。
Dev C++ 5.11及其之前的版本默认是不支持c++11新标准的,解决方案也很简单,在菜单栏点开Tools -> Compile Options,添加编译指令-std=c++11即可,参见下图:
下面给出示例源码:
#include <iostream>
#include <list>
using namespace std;
int main(){list<int> l;cout<<"list的大小:"<<l.size()<<endl;//list的大小:0for (int i=0; i<10; i++)l.push_back(i); //尾部插入元素 尾插法cout<<"list的大小:"<<l.size()<<endl;//list的大小:10list<int>::iterator it = l.begin();while(it!=l.end()){cout<<*it<<" ";it++;}cout << endl;//0 1 2 3 4 5 6 7 8 9//list不能随机访问容器中的值,即不能it+5这样的操作。只能一个一个的走,即it++it=l.begin();it++;it++;it++;l.insert(it, 100); //100插入在链表第4个位置for (list<int>::iterator it = l.begin(); it != l.end(); it++)cout<<*it<<" ";cout<<endl;//0 1 2 100 3 4 5 6 7 8 9l.clear(); cout<<"list的大小:"<<l.size()<<endl;//list的大小:0for (int i=0; i<10; i++)l.push_front(i); //尾部插入元素 尾插法cout<<"list的大小:"<<l.size()<<endl;//list的大小:10for(list<int>::iterator it=l.begin(); it!=l.end(); it++)cout<<*it<<" ";cout<<endl; //9 8 7 6 5 4 3 2 1 0list<int>::iterator it1 = l.begin();list<int>::iterator it2 = l.begin();it2++;it2++;it2++;//要想删除一个区间段。只能用指针++一步一步的指向那个末尾位置,不能直接l.begin()+3l.erase(it1, it2); //删掉的是区间[it1,it2) for (list<int>::iterator it=l.begin(); it!=l.end(); it++)cout<<*it<<" ";cout<<endl;//6 5 4 3 2 1 0l.insert(l.begin(), 100);l.insert(l.begin(), 100);l.insert(l.begin(), 100);l.erase(l.begin()); //删除该位置的元素for (list<int>::iterator it=l.begin(); it!=l.end(); it++)cout<<*it<<" ";cout<<endl;//100 100 6 5 4 3 2 1 0cout<<"链表中的第一个元素:"<<l.front()<<endl; //链表中的第一个元素:100cout<<"链表中的最后一个元素:"<<l.back()<<endl; //链表中的最后一个元素:0l.remove(100); //移除所有100元素的值 removefor (list<int>::iterator it=l.begin(); it!=l.end(); it++)cout<<*it<<" ";cout<<endl;//6 5 4 3 2 1 0
}
运行之:
相关文章:

C++实现链表
C实现链表 众所周知,C/C语言实现的链表是由一个一个的结点构成,每个结点分为数据域和指针域,指针域中存储了其后继结点的地址,通过地址来访问下一个结点。 链表是一系列节点串联形成的数据结构,链表存储有序的元素集合…...

MySQL索引篇
文章目录说明:索引篇一、索引常见面试题按数据结构按物理存储分类按字段特性分类按字段个数分类索引缺点:什么时候适用索引?什么时候不需要创建索引?常见优化索引的方法:发生索引失效的情况:二、从数据页角…...

Ardiuno-交通灯
LED交通灯实验实验器件:■ 红色LED灯:1 个■ 黄色LED灯:1 个■ 绿色LED灯:1 个■ 220欧电阻:3 个■ 面包板:1 个■ 多彩杜邦线:若干实验连线1.将3个发光二极管插入面包板,2.用杜邦线…...
Leetcode.1234 替换子串得到平衡字符串
题目链接 Leetcode.1234 替换子串得到平衡字符串 Rating : 1878 题目描述 有一个只含有 Q, W, E, R四种字符,且长度为 n 的字符串。 假如在该字符串中,这四个字符都恰好出现 n/4次,那么它就是一个「平衡字符串」。 给你一个这样…...
聚类算法之K-means算法详解
文章目录 什么是聚类k-means算法简介牧师-村民模型算法步骤伪代码流程描述手动实现优缺点优点缺点算法调优与改进数据预处理合理选择 K 值手肘法Gap Statistic(间隔统计量)轮廓系数法(Silhouette Coefficient)Canopy算法拍脑袋法采用核函数K-means++ISODATA参考文献<...
电话呼入/呼出CSFB流程介绍
MO CSFB 注册的LAI跟SYS_INFO不同会触发LU流程;LU流程结束后,判断LOCATION UPDATING ACCEPT消息中的"Follow-on proceed"参数状态。(1)如果IE消息中有"Follow-on proceed",终端直接发送CM Service Request; (2)如果IE消息中没有"Follow-on procee…...

【比赛合集】9场可报名的「创新应用」、「程序设计」大奖赛,任君挑选!
CompHub 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…)比赛。本账号同时会推送最新的比赛消息,欢迎关注!更多比赛信息见 CompHub主页 或 点击文末阅读原文以下信息仅供参考,以比赛官网为准目录创新应用赛&…...

剑指 Offer 27. 二叉树的镜像
剑指 Offer 27. 二叉树的镜像 难度:easy\color{Green}{easy}easy 题目描述 请完成一个函数,输入一个二叉树,该函数输出它的镜像。 例如输入: 镜像输出: 示例 1: 输入:root [4,2,7,1,3,…...

RPC编程:RPC概述和架构演变
RPC编程系列文章第一篇一:引言1:本系列文章的目标2:RPC的概念二:架构的演变过程1:单体架构1):概念2):特点3):优缺点2:单体架构水平扩展1):水平拓展的含义2)&a…...
神经网络训练时只对指定的边更新参数
在神经网络中,通常采用反向传播算法来计算网络中各个参数的梯度,从而进行参数更新。在反向传播过程中,所有的参数都会被更新。因此,如果想要只更新指定的边,需要采用特殊的方法。 一种可能的方法是使用掩码࿰…...
Python列表list操作-遍历、查找、增加、删除、修改、排序
在使用列表的时候需要用到很多方法,例如遍历列表、查找元素、增加元素、删除元素、改变元素、插入元素、列表排序、逆序列表等操作。 1、遍历列表 遍历列表通常采用for循环的方式以及for循环和enumerate()函数搭配的方式去实现。 1ÿ…...

Python开发-学生管理系统
文章目录1、需求分析2、系统设计3、系统开发必备4、主函数设计5、 学生信息维护模块设计6、 查询/统计模块设计7、排序模块设计8、 项目打包1、需求分析 学生管理系统应具备的功能: ●添加学生及成绩信息 ●将学生信息保存到文件中 ●修改和删除学生信息 ●查询学生…...
大数据处理 - Trie树/数据库/倒排索引
Trie树Trie树的介绍和实现请参考 树 - 前缀树(Trie)适用范围: 数据量大,重复多,但是数据种类小可以放入内存基本原理及要点: 实现方式,节点孩子的表示方式扩展: 压缩实现。一些适用场景:寻找热门查询: 查询串的重复度比较高&#…...

jjava企业级开发-01
一、Spring容器演示 采用Spring配置文件管理Bean 1、创建Maven项目 修改项目的Maven配置 2、添加Spring依赖 在Maven仓库里查找Spring框架(https://mvnrepository.com) 同上添加其他依赖 <?xml version"1.0" encoding"UTF-8…...
「事务一致性」事务afterCommit
在事务还没有执行完消息就已经发出去了, 导致后续的一些数据或逻辑上的问题产生。场景如下:异步-记录日志:当事务提交后,再记录日志。发送mq消息:只有业务数据都存入表后,再发mq消息。方案1. 利用TransactionSynchroni…...

【深度学习编译器系列】2. 深度学习编译器的通用设计架构
在【深度学习编译器系列】1. 为什么需要深度学习编译器?中我们了解到了为什么需要深度学习编译器,和什么是深度学习编译器,接下来我们把深度学习编译器这个小黑盒打开,看看里面有什么东西。 1. 深度学习编译器的通用设计架构 与…...

图解操作系统
硬件结构 CPU是如何执行程序的? 图灵机的工作方式 图灵机的基本思想:用机器来模拟人们用纸笔进行数学运算的过程,还定义了由计算机的那些部分组成,程序又是如何执行的。 图灵机的基本组成如下: 有一条「纸带」&am…...

【发版或上线项目保姆级心得】
第一步:先在正式环境创建数据库/新增表格或者字段 在数据库表中增加字段/表格,不会报错。 但是切记不要过早数据库字段/表格或者删除字段/表格 第二步:修改配置文件 先将正式环境需要的配置给写好,包括但不仅限于数据库配置、…...
Python数据分析-pandas库入门
pandas 库概述pandas 提供了快速便捷处理结构化数据的大量数据结构和函数。自从2010年出现以来,它助使 Python 成为强大而高效的数据分析环境。pandas使用最多的数据结构对象是 DataFrame,它是一个面向列(column-oriented)的二维表…...

MacBook Pro 恢复出厂设置
目录1.恢复出厂设置1.1 按Command-R 键1.2 macOS 实用工具1.3 从 macOS 恢复功能的实用工具窗口中选择“磁盘工具”,然后点按“继续”1.4 在“磁盘工具”边栏中选择您的设备或宗卷。1.5 点按“抹掉”按钮或标签页1.6 抹掉OS X HD - 数据 完成1.7 抹掉 OS X HD1.8 查…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...