单链表(详解)

目录

  • 一.链表的介绍
  • 二.链表的各种方法
  • 单链表的结构
  • 初始化链表
  • 为链表开辟新节点
  • 打印链表
  • 尾插
  • 头插
  • 尾删
  • 头删
  • 查找
  • 指定位置之前插入
  • 指定位置之后插入
  • 删除(pos)节点
  • 删除节点(pos)之后的节点
  • 链表的销毁(节点被一个一个地销毁)

一.链表的介绍

链表在物理结构上不一定是连续的
逻辑结构上一定是连续的(可以通过前一个节点的指针找到下一个节点)

链表是由一个一个的节点组成的,
一个节点存储:指向下一个节点的指针和一个任意类型的数据
由此可以知道链表可以通过前一个节点的指针找到下一个节点,
各个节点就串联起来了

也可以把链表类比成一列火车,火车(链表)有一列列车厢(节点)组成

二.链表的各种方法

链表也是一种顺序结构(是线性表的一种),和顺序表(底层是数组)一样,
有许多的相似的方法

test.c --测试链表方法的文件
SList.h – 声明链表的实现方法和创建链表的结构
SList.c – 实现链表的方法

单链表的结构

typedef int DataType;
//将链表中的数据重命名为DataType,方便下次修改数据类型
typedef struct SListNode
{DataType data;//单链表中的数据struct SListNode* next;//指向下一个节点的指针
}SListNode;
//将链表的名字重命名,方便后续使用

初始化链表

//初始化 
//单链表的初始化没有节点
//所以单链表也可以不用初始化
//双向链表的初始化只有一个哨兵位
SListNode* SLInit(SListNode* phead)
{phead = (SListNode*)malloc(sizeof(SListNode));if (phead == NULL){perror("malloc");return NULL;}//申请成功phead->next = NULL;return phead;//返回申请申请成功的节点
}

为链表开辟新节点

//为链表开辟新节点
SListNode* SLByNode(DataType x)
{SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL){perror("malloc");return NULL;}//开辟成功newnode->next = NULL;newnode->data = x;return newnode;//把开辟的新节点返回
}

打印链表

//打印链表
void SLPrint(SListNode* phead)
{SListNode* pcur = phead;while (pcur)// 节点不为空,打印{printf("%d->", pcur->data);//打印当前节点pcur = pcur->next;//指针往下走}printf("NULL\n");//打印一行就换行
}

尾插

//尾插
void SLPushBack(SListNode** pphead,DataType x)
{assert(pphead);// *pphead指向第一个头结点//分为链表为空的尾插和不为空的尾插SListNode* newnode = SLByNode(x);if (*pphead == NULL){*pphead = newnode;//链表为空,头结点就是新节点的尾插}//链表不为空SListNode* pcur = *pphead;while (pcur->next){pcur = pcur->next;}//现在pcur的next指针为NULL, pcur newnode 链接上pcur->next = newnode;newnode->next = NULL;}

头插

//头插
void SLPushFront(SListNode** pphead, DataType x)
{assert(pphead);//链表不为空SListNode* newnode = SLByNode(x);//链接 newnode *ppheadnewnode->next = *pphead;*pphead = newnode;//newnode(*pphead)就是新的头节点
}

尾删

//尾删
void SLPopBack(SListNode** pphead)
{assert(pphead && *pphead);//链表不能为空,节点不能没有SListNode* pcur = *pphead;SListNode* prev = *pphead;if (pcur->next == NULL){//只有一个节点free(*pphead);*pphead = NULL;}else {//有二个及以上节点while (pcur->next!=NULL){prev = pcur;pcur = pcur->next;}//prev指向了最后一个节点的前一个节点//pcur指向了最后一个节点prev->next = NULL;free(pcur);pcur = NULL;}}

头删

//头删
void SLPopFront(SListNode** pphead)
{assert(pphead&&*pphead);//空链表和空节点不能删SListNode* pcur = (*pphead)->next;//->的优先级比*高,所以打括号//先把头节点的下一个节点存起来free(*pphead);//释放头节点*pphead = pcur;//头节点的下一个节点是新的头结点
}

查找

//查找
SListNode* SLCheck(SListNode* phead,DataType x)
{assert(phead);SListNode* pcur = phead;while (pcur){if (pcur->data == x){//找到了return pcur;//返回存储x的节点}pcur = pcur->next;}//没找到return NULL;
}

指定位置之前插入

//在指定位置之前插入数据
void SLInsertF(SListNode** pphead,SListNode* pos,DataType x)
{assert(pphead&&*pphead);//因为有指定位置,链表不能为空SListNode* newnode = SLByNode(x);//为插入的数据开辟一个节点SListNode* pcur = *pphead;while (pcur->next != pos){pcur = pcur->next;}//找到pos之前的数据,在pos前插入数据newnode->next = pcur->next;//右边赋值给左边,pcur->next不会被改变pcur->next = newnode;//pcur还是指向它的下一个节点//这两句不能交换
}

指定位置之后插入

//在指定位置之后插入数据
void SLInsertB(SListNode* pos, DataType x)
{assert(pos);//不能没有节点SListNode* newnode = SLByNode(x);//为新节点申请一个节点newnode->next = pos->next;//右边赋值给左边,pcur->next不会被改变pos->next = newnode;//pcur还是指向它的下一个节点//这两句不能交换
}

删除(pos)节点

//删除pos节点
void SLErase(SListNode** pphead, SListNode* pos)
{assert(pphead && pos);assert(*pphead);//链表为空和没有节点不能删除,指定删除的节点没有,不能删除//可以分为只有一个节点和两个及以上节点两种情况if ((*pphead)->next == NULL){//只有一个节点free(*pphead);*pphead = NULL;}else{//有两个及以上节点//先找到pos节点的前一个节点SListNode* pcur = *pphead;SListNode* plist = pos->next;while (pcur->next != pos){pcur = pcur->next;}//找到pos之前的节点pcur->next = pos->next;//  pcur  pos  pos->next 链接//pcur指向pos的下一个节点,把pos释放free(pos);pos = NULL;}
}

删除节点(pos)之后的节点

等价于删除pos的下一个节点

//删除pos之后的节点
void SLEraseB(SListNode* pos)
{assert(pos);//不能没有节点SListNode* Del = pos->next->next;// pos pcur DelSListNode* pcur = pos->next;//pos的下一个节点pcurpos->next = Del;free(pcur);
}

链表的销毁(节点被一个一个地销毁)

//销毁链表
void SLDestroy(SListNode** pphead)
{//一个节点一个节点地释放assert(*pphead&&pphead);//(没有节点)空链表不用释放,pphead为NULLSListNode* pcur = *pphead;SListNode* prev = *pphead;while (pcur){prev = prev->next;//保存下一个节点free(pcur);pcur = prev;//pcur就指向下一个节点}*pphead = NULL;//临时变量pcur和*pphead指向同一块空间,把临时变量释放掉,还要把*pphead置为NULL//防止野指针*pphead不用的时候
}

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

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

相关文章

linux调试-访问物理地址

1. devmem 方式 rootraspberrypi:/home/niyu# busybox devmem 0x7e215000 8 0xa rootraspberrypi:/home/niyu# busybox devmem 0x7e215000 8 0x0A rootraspberrypi:/home/niyu# busybox devmem 0x7e215000 8 0xb rootraspberrypi:/home/niyu# busybox devmem 0x7e21500…

如何制作个性又美观的二维码?自定义Logo、样式,还能一键复用

草料二维码提供基础的二维码美化设置,包含Logo、颜色、码点码眼、容错、添加文字等设置。 还提供150标签样式,标签样式中所有内容,包括LOGO、背景、字段数量等,均可修改。 同时,支持将样式保存到账号下,方…

磨损对输送带安全的影响

磨损对输送带安全的影响 在工业生产中,输送带作为重要的物流传输设备,广泛应用于煤炭、化工、冶金、电力、建材等多个行业。然而,输送带在使用过程中不可避免地会出现磨损现象,这不仅会影响其使用寿命,还可能对生产安…

vue-router学习3:路由传参方式

路由传参的方式主要有两种:query传参和params传参。 1. Query 传参 首先,在路由配置文件中(通常是 router/index.ts),定义你的路由: import { createRouter, createWebHistory } from vue-router; imp…

从递归角度串联二叉树-图论-动态规划

一、深度理解二叉树的前中后序遍历 二叉树遍历框架如下: void traverse(TreeNode* root) {if (root nullptr) {return;}// 前序位置traverse(root->left);// 中序位置traverse(root->right);// 后序位置 }先不管所谓前中后序,单看 traverse 函数…

常见的工业路由器访问问题

A:工业路由器已经设置了pptp怎么访问路由下面的电脑 1. 确认PPTP VPN设置:首先,确保PPTP VPN服务器在工业路由器上已正确设置,并且处于活动状态。这包括确保VPN服务器的IP地址、端口、用户名和密码等设置正确无误。 2. 连接到VP…

硬件24、嘉立创EDA丝印的优化和调整

1、调整全部丝印的属性 先选中一个丝印,然后右键点击它,选择查找,然后选择查找全部 选择查找全部这个时候可以设置所有丝印在元件的位置了,布局-》属性位置,位号,属性位置设置为上边,这时丝印就…

Linux红帽(RHCE)认证学习笔记 - (2)用户组和用户的管理

二、用户组和用户密码和用户组密码管理 ⽤户的添加(useradd) ⽤户的删除(userdel) ⽤户的修改(usermod) ⽤户的查看(查看/etc/passwd) id用户信息都是存放在 /etc/passwd 用户密码都是存放在 /etc/shadow 用户组信息都是存放在 /etc/group 用户组密码都是存放在 /etc/…

五、yolov8 tensorRT c++部署及接口封装(保姆级教程附源码)

采用 CTensorRT来部署深度学习模型有以下几个优点: 高性能推理:TensorRT是一个高性能的深度学习推理(Inference)优化器,专门为NVIDIA GPU硬件平台设计,能够提供低延迟、高吞吐量的模型推理性能。这意味着在…

深度学习pytorch实战4---猴逗病识别·

>- **🍨 本文为[🔗365天深度学习训练营](https://mp.weixin.qq.com/s/0dvHCaOoFnW8SCp3JpzKxg) 中的学习记录博客** >- **🍖 原作者:[K同学啊](https://mtyjkh.blog.csdn.net/)** 引言 1.复习上周并反思 K同学针对大家近…

STM32 float浮点数转换成四个字节

float浮点数转换成四个字节 在C或C中,联合体(union)是一种特殊的数据结构,它允许在相同的内存位置存储不同的数据类型。联合体中的所有成员共享同一块内存区域,这意味着同一时间内,联合体只能保存其中一个…

python爬虫 - 爬取html中的script数据(36kr.com新闻信息)

文章目录 1. 分析页面内容数据格式2. 使用re.findall方法,爬取新闻3. 使用re.search 方法,爬取新闻 1. 分析页面内容数据格式 打开 https://36kr.com/ 按F12(或 在网页上右键 --> 检查(Inspect)) 找…

c++初阶——类和对象(中)

大家好,我是小锋,我们今天继续来学习类和对象。 类的6个默认成员函数 我们想一想如果一个类什么都没有那它就是一个空类,但是空类真的什么都没有吗? 其实并不是,任何类在什么都不写时,编译器会自动生成以…

电脑提示丢失iutils.dll怎么办?一分钟教你搞定dll丢失问题

在计算机世界中,DLL(Dynamic Link Library,动态链接库)文件扮演着至关重要的角色,它们如同乐高积木中的基础模块,不同程序通过调用这些模块来实现各种功能。其中,iutils.dll就是这样一款不可或缺…

transformer 最简单学习3, 训练文本数据输入的形式

1、输入数据中,源数据和目标数据的定义 def get_batch(source,i):用于获取每个批数据合理大小的源数据和目标数据参数source 是通过batchfy 得到的划分batch个 ,的所有数据,并且转置列表示i第几个batchbptt 15 #超参数,一次输入多少个ba…

Java 异常

异常概念 Java异常是程序在执行过程中发生的错误事件,它打断了正常的流程控制流。 在Java中,异常被定义为一种特殊的对象,它们可以被抛出、捕获和处理。以下是关于Java异常的一些详细信息: 异常分类:Java中的异常分…

AI大模型评测问题集合

文章目录 编程能力测试工作场景价值观测试生活场景数学能力测试中文能力测试编程能力测试 第一题:Google 面试题,Python 题目 给定一组不同的整数数组,给出一个算法对这些整数进行随机排序,使每个重 排序方法的可能性相等。换句话说,给定一副牌,你要如何洗牌才能确保牌的…

vue2 mixin的用法

在 Vue 2 中,mixin 是一种分发 Vue 组件中可复用功能的非常灵活的方式。一个 mixin 对象可以包含任意组件选项。当组件使用 mixin 对象时,所有 mixin 对象的选项将被“混合”进入该组件本身的选项。 下面是如何在 Vue 2 中使用 mixin 的基本步骤&#x…

聚类分析字符串数组

聚类分析字符串数组 对多个字符串进行聚类分析旨在根据它们之间的相似度将这些字符串划分成若干个类别,使得同一类别内的字符串彼此相似度高,而不同类别间的字符串相似度低 小结 数据要清洗。清洗的足够准确,可能不需要用聚类分析了数据要…

C++ //练习 13.34 编写本节所描述的Message。

C Primer(第5版) 练习 13.34 练习 13.34 编写本节所描述的Message。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块 /*************************************************************************>…