1.单链表

1.数据结构简介

程序=数据结构+算法

数据

数据(data)是客观事物的一个符号表示

数据元素(data element)是数据的基本单位,一 个数据元素可以由若干个数据项(data item)组成。数据项是数据不可分割的最小单位。

数据结构

是数据元素之间相互关系的集合

逻辑结构 逻辑层面:同窗

数据元素之间存在的逻辑关系

集合

数据结构中的元素之间除了“同属一个集合”的相互关系外,别无其他关系

**线性结构: **排队:一列

数据结构中的元素存在一对一的相互关系,如数组(索引)、链表(指针)、栈和队列(操 作顺序)等。

**树形结构: **排队:方队

数据结构中的元素存在一对多的相互关系,如树和二叉树(一个父节点对应多个子节点) 等

图形结构:

数据结构中的元素存在多对多的相互关系,如图(每个节点都可以与多个节点相连,同时它 也可以被多个节点所连接。)等

物理结构(存储结构)

数据的物理结构或存储结构是数据在计算机中的存储表示或实现

顺序结构:

数据元素在物理位置上相邻,通过元素的存储地址来表示数据元素之间的逻辑关系,比如数组

链表结构:

对逻辑上相邻的元素不要求其物理位置相邻,元素间的逻辑关系通过附设的指针域来表示

索引结构:

为每个数据结构建立索引表,每个数据元素占用表中的一项,每个表项通常包含关键字和地 址指针 map

散列结构(哈希结构):

根据数据元素的关键字通过哈希函数计算出一个数值用作数据元素的存储地 址,以实现快速查找和插入

2.线性表(逻辑上)

它表示元素之间具有一对一的相邻关系的数据集合。

线性表的数据元素可以是各种各样、但是同一个线性表中的元素必须是相同类型

特性:

存在唯一的一个被称为:"第一个"的数据元素

存在唯一的一个被称为:“最后一个”的数据元素

除了第一个元素之外,集合中的每一个数据元素前均只有一个节点

除了最后一个元素之外,集合中的每一个数据元素后均只有一个节点

线性表可以是顺序结构也可以是链表结构.。顺序存储的线性表通常通过数组(Array)实现,而链式存储的线性表则通过链表(Linked List)实现

2.1顺序存储的线性表(数组)

在C++中,数组是最简单的顺序存储的线性表实现。数组中的元素在内存中连续存放,每个元素可以通 过索引(或下标)快速访问。

例子:

#include <iostream>  
using namespace std;  
int main() 
{  
int arr[5] = {1, 2, 3, 4, 5}; // 定义一个整型数组,大小为5  
for(int i = 0; i < 5; i++) 
{  
cout << arr[i] << " "; // 访问并打印数组中的每个元素  
}  

上述结构存在一些问题问题:我们在定义数组的时候,需要规定数组的长度,定义好了就不能修改了。

int a[10]; // 定义了一个元素为int类型的数组,数组大小为10个int类型
// 修改数组的大小
a = malloc(sizeof(int) * 100); // 不行的,因为a是常量指针,不能改变a指向。

2.1链式存储的线性表(链表)*

链表的定义:链表就是由一个或者是多个包含指针成员变量结构体.

struct 类型名
{
类型1 变量1; // 数据1...
类型n 变量n; // 数据nstruct 类型名 *指针变量; // 存储数据元素的地址。在单链表结构里面,存储下一个数据元素的内
存地址
}

通过其指针变量来指向其他同类型结构体内存 地址来形成一种逻辑上的链式的数据结构。我们把每一个结构体实例(变量/空间)称为该链表的:节点 (node)

链表中的每个元素都是一个节点(Node),节点之间通过指针连接。链表分为单向链表、双向链表和循 环链表等多种类型。

***问题1:***为什么是 struct (结构体)?

因为包含指针成员,,记录下一个元素的地址。而其他数据类型可能与指针类型不同,因此需要能够存放 不同数据类型的结构,因此是结构体。

问题2::为什么指针成员是 struct 类型名 *(当前结构体指针类型)?

因为需要存放下一个元素的地址,而下一个元素是 struct 类型名类型的。如果是 struct 类型名,要为当前类型分配空间,因为结构题没有定义完全**,没有办法分配内存**。

struct 类型名 *的话是指针,无论什么类型指针都是分配八个字节。

特殊的节点

***头节点:***是链表中唯一一个没有前节点、只有后节点的节点,是整个链表的第一个节点(开端)。

***尾节点:***是链表中唯一一个没有后节点,只有前节点的节点,是整个链表的最后一个节点(结 尾)。因此尾节点的指针指向空( NULL ),以保证尾节点的指针成员不会成为野指针。可以通过判断 指针是否指向空来判断是不是查询到了最后一个元素。

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

2.3链表的创建过程

(单链表 Singly Linked List)

2.3.1 定义链表节点结构

首先,需要定义一个链表节点的结构体,该结构体至少包含两个成员:存储数据的部分(数据域)和指 向下一个节点的指针(指针域)

//定义链表节点结构
typedef struct listNode{int val; // 整型数据  
struct li
2.3.2 初始化链表头指针

在创建链表之前,需要初始化链表的头指针。头指针是一个指向链表第一个节点的指针。如果链表为 空,则头指针通常设置为 NULL 。

//初始化链表头指针
ListNode* head = NULL; // 初始化链表为空链表  

在这里插入图片描述

创建并添加节点到链表

接下来,可以通过创建新的节点并将其插入到链表中来构建链表。插入操作可以根据需要在链表的不同 位置进行(如头部、尾部或特定位置)。

2.3.3 创建节点
//创建节点 value新节点存储的数据
//可以是普通结构体变量,但是之后用到的要&listNode *createNode(int value)
{listNode *newNode=new listNode{ value};//动态分配内存,设置新节点的值if(newNode!=NULL){newNode->next=NULL;//新节点的next指针初始化为NULL,没有后面的元素}return newNode;//此处可以返回局部变量的地址是因为newNode在堆区,生命周期在delete后才结
束}

推荐创建节点时用 new/malloc 在堆区申请节点,而不是栈区。

因为堆区空间由程序员维护,可以控制 其声明周期,

而栈区内存空间由编译器维护,无法控制生命周期,有可能内存空间被意外收回。

2.3.4 插入节点

如果头节点为空,新创建的节点即为头节点

在这里插入图片描述

头插 直接让新节点指向头节点,那么新节点就变成了新的头节点

//头插 2-3   ->   1-2-3直接让新节点指向头节点,新节点变成新的头节点
//头部添加节点//希望形参影响实参
listNode *insertNodeAtHead(listNode*head/*头节点的地址*/,int value/*数据*/)//head是一级指针要有返回值,
// 因为是值传递,用返回值更新head指向 二级指针不需要 地址传递
{if(head==NULL){//链表是空,新节点就是头节点head=createNode(value);return head;}![屏幕截图 2025-03-31 065839](D:\笔记\4-2 数据结构 (复习笔记)\屏幕截图 2025-03-31 065839.png)//创建新节点listNode *newHead=createNode(value);//让新节点指向头节点newHead->next=head;//返回新的头节点return newHead;}

添加头节点 如果为空直接创建,新节点就是头节点,如果不为空直接让新节点指向头节点,新节点变成新的头节
在这里插入图片描述

尾部插入:如果要在链表尾部添加节点,需要遍历链表直到最后一个节点,然后将最后一个节 点的next指针指向新节点

//尾部添加节点
listNode *insertNodeAtTail(listNode*head,int value)
{if(head==NULL){//链表是空,新节点就是头节点head=createNode(value);}else{  //定义中间变量,找到尾节点listNode *current=head;while( current->next==NULL){//向后遍历current==current->next;}//current指向尾节点 ,尾节点next指向新节点current->next=createNode(value);}//返回头节点 在调用该函数时要用head接受该返回值来更新头节点return head;}

在这里插入图片描述

中间插入::按照一定规则在某个位置插入元素

//中间插入(从小到大)
//head:链表的头节点 newNode:插入节点
listNode *insertNodeAtMiddle(listNode*head,int value)
{if(head==NULL){//链表是空,新节点就是头节点head=createNode(value);}else{//比头节点小, 例如:2-3 头插if(value<head->val);{// 头插head=insertNodeAtHead(head,value);//结束return head;}//前面的节点listNode*first=head;//下一个的节点listNode*second=first->next;/*在两者之间插入 只需要在first和second之间插入 链表只有一个元素 first:1 second null 直接掺入first后只要second为空 直接尾插*///first不是尾节点while(second){//判断相邻两个节点的大小关系if(value>first->val&&value<second->val){listNode *newNode =createNode(value);//创建节点//一定是先连接后面节点,在连接前面节点,因为节点只记录了下一个节点的地址信// 息,如果先连接前面,则后面的地址信息就丢失了//先连接后面的节点,即先连接2-3newNode->next=second;//然后让前面的节点指向新节点,即1-2first->next=newNode;return head;}//向后遍历first=second;second=second->next;}//1-2-3   4  尾插head=insertNodeAtTail(head,value);}}

在这里插入图片描述

附加:

值传递和地址传递区别-------形参修改会不会影响实参

实参 int 形参 int 值传递 形参不会影响实参

实参 int 形参 int* 地址传递 形参会影响实参

实参 int* 形参 int* 值传递* 形参不会影响实参

2.3.5 删除链表中的节点

将链表中某个节点删除,删除时要注意,创建节点时申请了内存,删除时要释放内存以防止内存泄露, 同时将指针置为空

//删除节点listNode  *deteleNode(listNode*head,int value)
{if (head==NULL){//如果链表是空,结束return 0;}else if(value==head->val){//纪律新的头节点listNode*newHead=head->next;//删除头节点。防止内存泄漏delete head;//更新头节点head=newHead;}//非头节点else{listNode*current=head;//遍历链表while(current->next){//只使用一个指针 删除当前节点的下一个节点if(current->next->val==value){// 当前节点的下一个节点 1-2-3 delete 2 变成1-3listNode* needDeleNode =current->next;//中间节点if(current->next->next ){//更新节点指向current->next= current->next->next;}//尾节点1-2 删除2 变1else{![屏幕截图 2025-03-31 070326](D:\笔记\4-2 数据结构 (复习笔记)\屏幕截图 2025-03-31 070326.png)current->next=NULL;}//删除下一个节点delete needDeleNode;needDeleNode=NULL;//提前结束return head;}current=current->next;//向后遍历}}//没有找到删除节点信息 return head;}

在这里插入图片描述

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

链表使用完毕记得要删除所有节点,防止内存泄露

void deinitNode(listNode*&head)
{//判断链表是不是空链表if(head==NULL){std::cout<<"链表为空"<<std::endl;return ;}listNode *current=head;while(current){//要删除的节点listNode *needDelNode=current;//遍历current=current->next;//删除节点delete  needDelNode;needDelNode=NULL;}}

2. 4 完整代码

#include <iostream>using namespace std;
//定义链表节点结构
typedef struct listNode
{int val;//整形数据struct listNode *next;//指向下一个节点的指针//listNode *next;不行,因为listNode没有定义出来
}listNode;//创建节点 value新节点存储的数据
//可以是普通结构体变量,但是之后用到的要&
listNode *createNode(int value)
{listNode *newNode=new listNode{value};//动态分配内存,设置新节点的值if(newNode!=NULL){newNode->next=NULL;//新节点的next指针初始化为NULL,没有后面的元素}return newNode;}//头插 2-3   ->   1-2-3直接让新节点指向头节点,新节点变成新的头节点
//头部添加节点
//希望形参影响实参
listNode *insertNodeAtHead(listNode*head,int value)//head是一级指针要有返回值,// 因为是值传递,用返回值更新head指向 二级指针不需要 地址传递
{if(head==NULL){//链表是空,新节点就是头节点head=createNode(value);return head;}//创建新节点listNode *newHead=createNode(value);//让新节点指向头节点newHead->next=head;//返回新的头节点return newHead;}
//尾部添加节点
listNode *insertNodeAtTail(listNode*head,int value)
{if(head==NULL){//链表是空,新节点就是头节点head=createNode(value);}else{  //定义中间变量,找到尾节点listNode *current=head;while( current->next){//向后遍历current=current->next;}//current指向尾节点 ,尾节点next指向新节点current->next=createNode(value);}//返回头节点 在调用该函数时要用head接受该返回值来更新头节点return head;}//中间插入(从小到大)
//head:链表的头节点 newNode:插入节点
listNode *insertNodeAtMiddle(listNode*head,int value)
{if(head==NULL){//链表是空,新节点就是头节点head=createNode(value);}else{//比头节点小, 例如:2-3 头插if(value<head->val){// 头插head=insertNodeAtHead(head,value);//结束return head;}//前面的节点listNode*first=head;//下一个的节点listNode*second=first->next;/*在两者之间插入 只需要在first和second之间插入 链表只有一个元素 first:1 second null 直接掺入first后只要second为空 直接尾插*///first不是尾节点while(second){//判断相邻两个节点的大小关系if(value>first->val&&value<second->val){listNode *newNode =createNode(value);//创建节点//一定是先连接后面节点,在连接前面节点,因为节点只记录了下一个节点的地址信// 息,如果先连接前面,则后面的地址信息就丢失了//先连接后面的节点,即先连接2-3newNode->next=second;//然后让前面的节点指向新节点,即1-2first->next=newNode;return head;}//向后遍历first=second;second=second->next;}// listNode*current=head;// while(value)//1-2-3   4  尾插head=insertNodeAtTail(head,value);}return head;
}
//删除节点listNode  *deteleNode(listNode*head,int value)
{if (head==NULL){//如果链表是空,结束return 0;}else if(value==head->val){//纪律新的头节点listNode*newHead=head->next;//删除头节点。防止内存泄漏delete head;//更新头节点head=newHead;}//非头节点else{listNode*current=head;//遍历链表while(current->next){//只使用一个指针 删除当前节点的下一个节点if(current->next->val==value){// 当前节点的下一个节点 1-2-3 delete 2 变成1-3listNode* needDeleNode =current->next;//中间节点if(current->next->next ){//更新节点指向current->next= current->next->next;}//尾节点1-2 删除2 变1else{delete current->next;current->next=NULL;}//删除下一个节点delete needDeleNode;needDeleNode=NULL;//提前结束return head;}current=current->next;//向后遍历}}//没有找到删除节点信息 return head;}//打印链表
void printtNode(listNode*head)
{//判断链表是不是空链表if(head==NULL){std::cout<<"链表为空"<<std::endl;return ;}listNode *current=head;//遍历while (current){std::cout<<current->val<<" ";   //输出内容current=current->next;}std::cout<<std::endl;
}//清空链表
void deinitNode(listNode*&head)
{//判断链表是不是空链表if(head==NULL){std::cout<<"链表为空"<<std::endl;return ;}listNode *current=head;while(current){//要删除的节点listNode *needDelNode=current;//遍历current=current->next;//删除节点delete  needDelNode;needDelNode=NULL;}}int main()
{// 初始化链表头指针listNode *head=NULL;//初始化链表为空链表//head=insertNodeAtHead的返回值//插入head=insertNodeAtMiddle(head,1);//头head=insertNodeAtMiddle(head,5);//尾head=insertNodeAtMiddle(head,3);//中间head=insertNodeAtMiddle(head,4);//中间head=insertNodeAtMiddle(head,2);//中间//打印printtNode(head);//删除head= deteleNode(head,1);//头head= deteleNode(head,5);//尾head= deteleNode(head,3);//中间printtNode(head);    //打印//清理链表deinitNode(head);//写完一个验证一个return 0;}

2.6.数组和链表的优缺点:

2.6.1数组

优点:

数组将元素按顺序存放在内存中,而且元素的内存占用都是相同的,因为它的地址连 续、所有可以通过下标快速的对数组元素进行访问。

缺点

有可能造成内存资源的浪费(固定大小) 数组元素的插入和删除比较复

2.6.2链表

优点:

插入和删除很方便

可以按需分配,不会造成空间的浪费(可有效利用碎片化空间)

缺点:

空间不是连续,访问相对数组而言效率要低很多碎片化比数组要高(尤其是当插入的数 据是无序的时候)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/78254.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/78254.shtml
英文地址,请注明出处:http://en.pswp.cn/web/78254.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GPU集群监控系统开发实录:基于Prometheus+Grafana的算力利用率可视化方案

一、科研场景下的GPU监控痛点 在深度学习模型训练、分子动力学模拟等科研场景中&#xff0c;GPU集群的算力利用率直接影响着科研效率。笔者在参与某高校计算中心的运维工作时&#xff0c;发现以下典型问题&#xff1a; 资源黑洞现象&#xff1a;多课题组共享GPU时出现"抢…

【计算机视觉】三维重建: MVSNet:基于深度学习的多视图立体视觉重建框架

MVSNet&#xff1a;基于深度学习的多视图立体视觉重建框架 技术架构与核心算法1. 算法流程2. 关键创新 环境配置与实战指南硬件要求安装步骤数据准备&#xff08;DTU数据集&#xff09; 实战流程1. 模型训练2. 深度图推断3. 点云生成 常见问题与解决方案1. CUDA内存不足2. 特征…

智能家居的OneNet云平台

一、声明 该项目只需要创建一个产品&#xff0c;然后这个产品里面包含几个设备&#xff0c;而不是直接创建几个产品 注意&#xff1a;传输数据使用到了不同的power&#xff0c;还有一定要手机先联网才能使用云平台 二、OneNet云平台创建 &#xff08;1&#xff09;Temperatur…

aidermacs开源程序使用 Aider 在 Emacs 中进行 AI 配对编程

一、软件介绍 文末提供程序和源码下载 Aidermacs 通过集成 Aider&#xff08;最强大的开源 AI 配对编程工具之一&#xff09;为 Emacs 带来了 AI 驱动的开发。如果您缺少 Cursor&#xff0c;但更喜欢生活在 Emacs 中&#xff0c;Aidermacs 提供了类似的 AI 功能&#xff0c;同…

加密算法(一)-对称加密(DES、AES、3DES、Blowfish、Twofish)一篇了解所有主流对称加密,轻松上手使用。

一、对称加密算法 对称加密算法采用相同的密钥来进行加密和解密操作。其优点是加密和解密速度快&#xff0c;不过密钥的管理和分发存在一定的安全风险。 1.1、DES(已不推荐使用) 这是早期的对称加密算法&#xff0c;密钥长度为 56 位。但由于密钥长度较短&#xff0c;如今已不…

深度优先VS广度优先:算法选择的核心逻辑与实战指南

摘要 深度优先搜索&#xff08;DFS&#xff09;与广度优先搜索&#xff08;BFS&#xff09;是图结构遍历与路径分析的基础算法&#xff0c;也是最常见的搜索框架&#xff0c;在路径规划、社交网络分析、游戏AI等领域均有广泛应用。本文从算法思想、数据结构选择、时空复杂度和…

2025深圳杯、东三省数学建模B题数模AI全网专业性第一

为什么选择使用我的数模AI&#xff1f; 1.轻松辅导学生 2.小白也能翻身碾压大佬 3.突破知识壁垒&#xff0c;缩短与大佬的差距&#xff0c;打破不公平的教学资源&#xff0c;扭转差距 4.辅助商业服务&#xff0c;成本低 5.大模型本身有一定随机性&#xff0c;所以也不用担心…

使用MGeo模型高精度实现文本中地址识别

一、功能与安装 1、模型地址 模型是阿里开发的门址高精度识别模型。 https://modelscope.cn/models/iic/mgeo_geographic_elements_tagging_chinese_base/summary 注意&#xff1a;不能自己安装包&#xff0c;没法解决依赖问题&#xff0c;直接按照官方要求安装下面的包&am…

【Vue】Vue与UI框架(Element Plus、Ant Design Vue、Vant)

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Vue 文章目录 1. Vue UI 框架概述1.1 主流Vue UI框架简介1.2 选择UI框架的考虑因素 2. Element Plus详解2.1 Element Plus基础使用2.1.1 安装与引入2.1.2 基础组件示例 2.2 Element Plus主题定制2.3 Element Plus的优缺点分析 3…

MLPerf基准测试工具链定制开发指南:构建领域特异性评估指标的实践方法

引言&#xff1a;基准测试的领域适配困局 MLPerf作为机器学习性能评估的"黄金标准"&#xff0c;其通用基准集在实际科研中常面临‌领域适配鸿沟‌&#xff1a;医疗影像任务的Dice系数缺失、NLP场景的困惑度指标偏差等问题普遍存在。本文通过逆向工程MLPerf v3.1工具…

好看的个人主页HTML源码分享

源码介绍 好看的个人主页HTML源码分享&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果 效果预览 源码获取 好看的个人主页HTML源码分享

mac word接入deepseek

网上大多使用Windows版word来接入deepseek&#xff0c;vba文件引入mac后&#xff0c;因底层工具不同&#xff0c;难以直接运行&#xff0c;例如CreateObject("MSXML2.XMLHTTP")无法创建&#xff0c;为此写了一版新的vba&#xff0c;基于mac底层工具来实现。 vba文件点…

React Native 入门 jsx tsx 基础语法

React Native 入门 jsx 基础语法 JSX 介绍 JSX (JavaScript XML) 是一种 JavaScript 的语法扩展&#xff0c;允许你在 JavaScript 文件中编写类似 HTML 的代码。它是 React 和 React Native 应用程序中用来描述 UI 的主要方式。 JSX 的特点 JSX 看起来像 HTML&#xff0c;但…

HDLBIT-程序(Procedures)

始终块(组合)【Always blocks(combinational)】 答案: Always blocks (clocked) 答案&#xff1a; module top_module(input clk,input a,input b,output wire out_assign,output reg out_always_comb,output reg out_always_ff );assign out_assigna^b;always(*)beginout_a…

值此五一劳动节来临之际,

值此五一劳动节来临之际&#xff0c;谨向全体员工致以节日的问候与诚挚的感谢&#xff01;正是你们的敬业与奋斗&#xff0c;成就了今天的成绩。愿大家节日愉快&#xff0c;阖家幸福&#xff0c;身体健康&#xff01; #北京先智先行科技有限公司 #先知AI #节日快乐

【经管数据】A股上市公司资产定价效率数据(2000-2023年)

数据简介&#xff1a;资产定价效率是衡量市场是否能够有效、准确地反映资产内在价值的重要指标。在理想的市场条件下&#xff0c;资产的市场价格应该与其内在价值保持一致&#xff0c;即市场定价效率达到最高。然而&#xff0c;在实际市场中&#xff0c;由于信息不对称、交易摩…

云蝠智能大模型智能呼叫:赋能零售行业服务,助力客户增长

在数字化浪潮席卷全球的今天&#xff0c;零售行业正面临前所未有的变革压力。消费者需求日益个性化、市场竞争愈发激烈&#xff0c;传统的人工客服模式已难以满足企业对高效触达、精准营销和极致体验的需求。而云蝠智能大模型智能呼叫系统&#xff0c;凭借其突破性的AI技术和深…

IP 互联网协议

IP&#xff08;Internet Protocol&#xff0c;互联网协议&#xff09;是网络通信中的核心协议之一&#xff0c;属于网络层协议。它的主要功能是提供数据包的寻址、路由以及传输。IP协议负责将数据从源主机传输到目标主机&#xff0c;并在网络中进行转发。在网络通信中&#xff…

报文三次握手对么٩(๑^o^๑)۶

论TCP报文三次握手机制的理论完备性与工程实践价值&#xff1a;基于网络通信协议栈的深度剖析 在计算机网络领域&#xff0c;传输控制协议&#xff08;TCP&#xff09;作为实现可靠数据传输的核心协议&#xff0c;其连接建立阶段的三次握手机制历来是网络工程与协议理论研究的…

HarmonyOS NEXT第一课——HarmonyOS介绍

一、什么是HarmonyOS 万物互联时代应用开发的机遇、挑战和趋势 随着万物互联时代的开启&#xff0c;应用的设备底座将从几十亿手机扩展到数百亿IoT设备。全新的全场景设备体验&#xff0c;正深入改变消费者的使用习惯。 同时应用开发者也面临设备底座从手机单设备到全场景多设…