数据结构---单链表的增删查改

前言:

经过了几个月的漫长岁月,回头时年迈的小编发现,数据结构的内容还没有写博客,于是小编赶紧停下手头的活动,补上博客以洗清身上的罪孽


目录

前言

           概念:

单链表的结构

我们设定一个哨兵位头节点给链表

单链表的基本操作

插入:

单链表的头插:

单链表的尾插:

单链表的遍历打印:从头节点开始,逐个访问链表中的每个节点,直到达到链表的末尾。

单链表的删除:删除链表中的指定节点,需要修改前一个节点的指针域以绕过被删除的节点。

单链表的头删:

单链表的尾删:

单链表元素的查找:根据给定的值或位置查找链表中的节点。

单链表的链表销毁:

验证:                                                                             

完整代码:

slist.h

slist.c

test.c

总结:


概念:

       单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素 (数据元素的映象) + 指针 (指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。单链表不要求逻辑上相邻的两个元素在物理位置上也相邻,因此不需要连续的存储空间。单链表是非随机的存储结构,即不能直接找到表中某个特定的结点。查找某个特定的结点时,需要从表头开始遍历,依次查找。 

单链表的结构

       在单链表中,每个节点由一个数据元素和一个指针构成。数据元素可以是任何类型,而指针则指向链表中的下一个节点。如果一个节点的指针为空(NULL),则表示它是链表的最后一个节点。单链表通常通过一个头指针来访问,头指针指向链表的第一个节点。

一个典型的单链表节点的结构可以用以下C语言代码表示:

typedef struct SListNode
{SLTDateType data;//数据域struct SListNode* next;// 指针域,指向下一个节点
}SListNode;
我们设定一个哨兵位头节点给链表
 SListNode* head = NULL; 
单链表的基本操作

单链表的基本操作包括初始化、遍历、插入、删除和查找等。

单链表的初始化:创建一个空链表,通常是通过创建一个头节点,其指针域为空。

插入:

在链表的指定位置插入一个新节点,需要修改前一个节点的指针域。

单链表的头插:

      断言确保头指针地址有效,先去动态申请一个新节点,然后将新节点的下一个节点指向头节点,将该新节点变为头节点,(不需要判断是否为空)


// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);//SListNode* start = *pplist;newnode->next = *pplist;*pplist = newnode;}
单链表的尾插:

      断言确保头指针地址有效,再申请一个新节点,分两种情况,当链表为空时,直接新节点就是头节点,链表不为空时,从头节点遍历到最后一个节点,将新节点设为最后一个节点的下一个节点

// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);SListNode* end = *pplist;if (*pplist != NULL) //链表非空{while (end->next != NULL)end = end->next;end->next = newnode;}else //链表为空{*pplist = newnode;}
}

     动态创建一个SListNode大小的节点,然后看看是否创建成功,成功后将数据放入该节点,并将该节点的下一个位置,置为空;


// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x) {SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL) {perror("malloc fail");return NULL;}// 初始化数据域和指针域newnode->data = x;newnode->next = NULL;return newnode;
}
单链表的遍历打印从头节点开始,逐个访问链表中的每个节点,直到达到链表的末尾。

  创建一个头指针,遍历一遍链表,每遍历一个节点,将这个节点的数据打印出来;

// 单链表打印
void SListPrint(SListNode* plist) {SListNode* head = plist;while (head != NULL) // 遍历链表直到NULL{printf("%d->", head->data);head = head->next;}printf("NULL\n");}

单链表的删除:删除链表中的指定节点,需要修改前一个节点的指针域以绕过被删除的节点。
单链表的头删:

      断言确保头指针地址有效,以及头指针不为空,将头指针保存好,然后将头指针置为头指针的下一个位置,再将保存的指针释放,并置为空

// 单链表头删
void SListPopFront(SListNode** pplist) {assert(pplist);assert(*pplist);SListNode* start = *pplist;*pplist = (*pplist)->next;free(start);start = NULL;}
单链表的尾删:

断言确保头指针地址有效,以及头指针不为空,分两种情况,

一种是头指针下一个节点为空,直接将其释放并置为空;

另外一种情况呢,就是设两个指针,找尾和尾的前驱节点,然后将尾节点释放并置为空,再将尾的前驱节点的下一个置为空

// 单链表的尾删
void SListPopBack(SListNode** pplist) {assert(pplist);assert(*pplist);//暴力检查是否为空链表,空链表不能删数据SListNode* end = *pplist;if ((*pplist)->next== NULL) {free(*pplist);*pplist = NULL;}else{//找尾SListNode* prev = *pplist;SListNode* tail = *pplist;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;//假如只有一个节点这里就会非法访问}}
单链表元素的查找:根据给定的值或位置查找链表中的节点。

        创建一个节点start将头节点指针复制一个副本,然后再指针不为空时不断将其指向下一个的同时,看看该节点的值与所要查找的值是否相等,相等返回该节点的结构体指针,当遍历完链表还没有找到,返回NULL;

// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x) {SListNode* start = plist;while (start != NULL) {if (start->data == x)return start;start = start->next;}return NULL;
}

单链表的链表销毁:

       先判断头指针是否为空,为空直接返回,不为空时创建一个start头指针储存头节点,再通过start遍历整个链表,在遍历过程中,将每一个节点释放掉,最后再将头节点释放,置为空;

//销毁链表
void SLTDestroy(SListNode** pphead) {if (*pphead == NULL) {return;}else {SListNode* start = *pphead;while (start != NULL) {SListNode* ne = start->next;free(start);start = ne;}free(*pphead);*pphead = NULL;}
}
验证:

我们再创建一个main的测试函数测试一下我们实现的单链表的各个功能有没有问题:

完整代码:
slist.h
#pragma once
// slist.h
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int SLTDateType;
typedef struct SListNode
{SLTDateType data;struct SListNode* next;
}SListNode;// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos);// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x);
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos);
void SLTDestroy(SListNode** pphead);
slist.c
#include "slist.h"
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x) {SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));if (newnode == NULL) {perror("malloc fail");return NULL;}newnode->data = x;newnode->next = NULL;return newnode;
}
// 单链表打印
void SListPrint(SListNode* plist) {SListNode* head = plist;while (head != NULL) {printf("%d->", head->data);head = head->next;}printf("NULL\n");}
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);SListNode* end = *pplist;if (*pplist != NULL) {while (end->next != NULL)end = end->next;end->next = newnode;}else {*pplist = newnode;}
}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x) {assert(pplist);SListNode* newnode = BuySListNode(x);//SListNode* start = *pplist;newnode->next = *pplist;*pplist = newnode;}
// 单链表的尾删
void SListPopBack(SListNode** pplist) {assert(pplist);assert(*pplist);//暴力检查是否为空链表,空链表不能删数据SListNode* end = *pplist;if ((*pplist)->next== NULL) {free(*pplist);*pplist = NULL;}else{//找尾SListNode* prev = *pplist;SListNode* tail = *pplist;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;//假如只有一个节点这里就会非法访问}}
// 单链表头删
void SListPopFront(SListNode** pplist) {assert(pplist);assert(*pplist);SListNode* start = *pplist;*pplist = (*pplist)->next;free(start);start = NULL;}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x) {SListNode* start = plist;while (start != NULL) {if (start->data == x)return start;start = start->next;}return NULL;
}
// 单链表在pos位置之后插入xvoid SListInsertAfter(SListNode* pos, SLTDateType x) {assert(pos);/*SListNode* pre = pos;*/SListNode* newnode = BuySListNode(x);SListNode* ne = pos->next;pos->next = newnode;newnode->next = ne;}
// 单链表删除pos位置之后的值void SListEraseAfter(SListNode* pos) {assert(pos);assert(pos->next);SListNode* pre = pos;SListNode* ne = pos->next->next;free(pos->next);pos->next = ne;
}// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x) {assert(pphead);assert(*pphead);assert(pos);if (*pphead == pos)SListPushFront(pphead, x);else {SListNode* ne = pos;SListNode* newnode = BuySListNode(x);SListNode* start = *pphead;while (start->next != pos) {start = start->next;if (start == NULL) {assert(0);return;}}start->next = newnode;newnode->next = ne;}}
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos) {assert(pphead);assert(*pphead);assert(pos);if (pos == *pphead)SListPopFront(pphead);else {SListNode* start = *pphead;while (start->next != pos) {start = start->next;if (start == NULL) {assert(0);return;}}start->next = pos ->next;free(pos);pos = NULL;}
}
void SLTDestroy(SListNode** pphead) {if (*pphead == NULL) {free(pphead); pphead = NULL;}else {SListNode* start = *pphead;while (start != NULL) {SListNode* ne = start->next;free(start);start = ne;}*pphead = NULL;}
}
test.c
#include "slist.h"int main() {SListNode* head = NULL; // 尾插SListPushBack(&head, 1);SListPushBack(&head, 2);SListPushBack(&head, 3);printf("链表内容(尾插入后):");SListPrint(head);// 头插SListPushFront(&head, 0);printf("链表内容(头插入后):");SListPrint(head);// 查找SListNode* found = SListFind(head, 2);if (found) {printf("找到节点:%d\n", found->data);}else {printf("未找到节点\n");}// 头删SListPopFront(&head);printf("链表内容(头删后):");SListPrint(head);// 尾删SListPopBack(&head);printf("链表内容(尾删后):");SListPrint(head);// pos插入SListInsertAfter(head, 4); printf("链表内容(插入4后):");SListPrint(head);// pos删除SLTErase(&head, head->next);printf("链表内容(删除第二个节点后):");SListPrint(head);// 销毁SLTDestroy(&head);//destroy后不能再访问// printf("链表内容(销毁后):");//SListPrint(head); return 0;
}

总结:

       本篇关于单链表的讲解到这里就结束啦,后续小编会带来更多精彩实用的内容,对你有帮助的可以点个赞,欢迎各位队列交流学习

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

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

相关文章

XSS靶场实战(工作wuwuwu)

knoxss knoxss Single Reflection Using QUERY of URL ——01 测试标签 <script>alert(666666)</script>——02: " <h1>test</h1>没有反应&#xff0c;查看源码 现在需要闭合双引号&#xff0c;我计划还是先搞标签 "><h1>tes…

基于 BERT 微调一个意图识别(Intent Classification)模型

基于 BERT 微调一个意图识别&#xff08;Intent Classification&#xff09;模型&#xff0c;你的意图类别包括&#xff1a; 查询天气获取新闻咨询想听音乐想添加备忘查询备忘获取家政服务结束对话增加音量减小音量其他 具体实现步骤&#xff08;详细版&#xff09; 1. 准备你…

SSM书籍管理(环境搭建)

整合SSM&#xff1a;SpringSpringMVCMybatis 环境要求&#xff1a;IDEA、MySQL5、Tomcat9、Maven3 数据库搭建 数据库准备以下数据用于后续实验&#xff1a;创建一个ssmbuild数据库&#xff0c;表books&#xff0c;该表有4个字段&#xff0c;并且插入3条数据用于后续。 CRE…

API文档生成与测试工具推荐

在API开发过程中&#xff0c;文档的编写和维护是一项重要但繁琐的工作。为了提高效率&#xff0c;许多开发者会选择使用API文档自动生成工具或具备API文档生成功能的API门户产品。选择能导入API文档的工具生成测试脚本, 本文将全面梳理市面上符合OpenAPI 3.0规范的文档生成工具…

linux修改环境变量

添加环境变量注意事项。 vim ~/.bashrc 添加环境变量时&#xff0c;需要source ~/.bashrc后才能有效。同时只对当前shell窗口有效&#xff0c;当打开另外的shell窗口时&#xff0c;需要重新source才能起效。 1.修改bashrc文件后 2.source后打开另一个shell窗口则无效&#xff…

springboot项目中,MySQL数据库转达梦数据库

前言 前段时间&#xff0c;公司要求要把某几个项目的数据库换成达梦数据库&#xff0c;说是为了国产化。我就挺无语的&#xff0c;三四年的项目了&#xff0c;现在说要换数据库。我一开始以为这个达梦数据库应该是和TIDB差不多的。 我之前做的好几个项目部署到测试服、正式服…

【Quest开发】透视环境下抠出身体并能遮挡身体上的服装

软件&#xff1a;Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件&#xff1a;Meta Quest3 仅针对urp管线 博主搞这个主要是想做现实里的人的变身功能&#xff0c;最后效果如下 可以看到虽然身体是半透明的&#xff0c;但是裙子依旧被完全遮挡了 原理是参考…

前端安全中的XSS(跨站脚本攻击)

XSS 类型 存储型 XSS 特征&#xff1a;恶意脚本存储在服务器&#xff08;如数据库&#xff09;&#xff0c;用户访问受感染页面时触发。场景&#xff1a;用户评论、论坛帖子等持久化内容。影响范围&#xff1a;所有访问该页面的用户。 反射型 XSS 特征&#xff1a;恶意脚本通过…

(第三篇)Springcloud之Ribbon负载均衡

一、简介 1、介绍 Spring Cloud Ribbon是Netflix发布的开源项目&#xff0c;是基于Netflix Ribbon实现的一套客户端负载均衡的工具。主要功能是提供客户端的软件负载均衡算法&#xff0c;将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时&…

大模型——使用coze搭建基于DeepSeek大模型的智能体实现智能客服问答

大模型——使用coze搭建基于DeepSeek大模型的智能体实现智能客服问答 本章实验完全依托于coze在线平台,不需要本地部署任何应用。 实验介绍 1.coze介绍 扣子(coze)是新一代 AI 应用开发平台。无论你是否有编程基础,都可以在扣子上快速搭建基于大模型的各类 AI 应用,并…

【计算机视觉】目标检测:深度解析YOLOv9:下一代实时目标检测架构的创新与实战

深度解析YOLOv9&#xff1a;下一代实时目标检测架构的创新与实战 架构演进与技术创新YOLOv9的设计哲学核心创新解析1. 可编程梯度信息&#xff08;PGI&#xff09;2. 广义高效层聚合网络&#xff08;GELAN&#xff09;3. 轻量级设计 环境配置与快速开始硬件需求建议详细安装步骤…

【SpringBoot】基于MybatisPlus的博客管理系统(1)

1.准备工作 1.1数据库 -- 建表SQL create database if not exists java_blog_spring charset utf8mb4;use java_blog_spring; -- 用户表 DROP TABLE IF EXISTS java_blog_spring.user_info; CREATE TABLE java_blog_spring.user_info(id INT NOT NULL AUTO_INCREMENT,user_na…

贵族运动项目有哪些·棒球1号位

10个具有代表性的贵族运动&#xff1a; 高尔夫 马术 网球 帆船 击剑 斯诺克 冰球 私人飞机驾驶 深海潜水 马球 贵族运动通常指具有较高参与成本、历史底蕴或社交属性的运动&#xff0c;而棒球作为一项大众化团队运动&#xff0c;与典型贵族运动的结合较为罕见。从以下几个角度探…

【Tauri2】035——sql和sqlx

前言 这篇就来看看插件sql SQL | Taurihttps://tauri.app/plugin/sql/ 正文 准备 添加依赖 tauri-plugin-sql {version "2.2.0",features ["sqlite"]} features可以是mysql、sqlite、postsql 进去features看看 sqlite ["sqlx/sqlite&quo…

全链路自动化AIGC内容工厂:构建企业级智能内容生产系统

一、工业化AIGC系统架构 1.1 生产流程设计 [需求输入] → [创意生成] → [多模态生产] → [质量审核] → [多平台分发] ↑ ↓ ↑ [用户反馈] ← [效果分析] ← [数据埋点] ← [内容投放] 1.2 技术指标要求 指标 标准值 实现方案 单日产能 1,000,000 分布式推理集群 内容合规率…

是否想要一个桌面哆啦A梦的宠物

是否想拥有一个在指定时间喊你的桌面宠物呢&#xff08;手动狗头&#xff09; 如果你有更好的想法&#xff0c;欢迎提出你的想法。 是否考虑过跟开发者一对一&#xff0c;提出你的建议&#xff08;狗头&#xff09;。 https://wwxc.lanzouo.com/idKnJ2uvq11c 密码:bbkm

Unity AI-使用Ollama本地大语言模型运行框架运行本地Deepseek等模型实现聊天对话(二)

一、使用介绍 官方网页&#xff1a;Ollama官方网址 中文文档参考&#xff1a;Ollama中文文档 相关教程&#xff1a;Ollama教程 使用版本&#xff1a;Unity 2022.3.53f1c1、Ollama 0.6.2 示例模型&#xff1a;llama3.2 二、运行示例 三、使用步骤 1、创建Canvas面板 具体…

从 BERT 到 GPT:Encoder 的 “全局视野” 如何喂饱 Decoder 的 “逐词纠结”

当 Encoder 学会 “左顾右盼”&#xff1a;Decoder 如何凭 “单向记忆” 生成丝滑文本&#xff1f; 目录 当 Encoder 学会 “左顾右盼”&#xff1a;Decoder 如何凭 “单向记忆” 生成丝滑文本&#xff1f;引言一、Encoder vs Decoder&#xff1a;核心功能与基础架构对比1.1 本…

数据结构入门:详解顺序表的实现与操作

目录 1.线性表 2.顺序表 2.1概念与结构 2.2分类 2.2.1静态顺序表 2.2.2动态顺序表 3.动态顺序表的实现 3.1.SeqList.h 3.2.SeqList.c 3.2.1初始化 3.2.2销毁 3.2.3打印 3.2.4顺序表扩容 3.2.5尾部插入及尾部删除 3.2.6头部插入及头部删除 3.2.7特定位置插入…

LeetCode热题100--53.最大子数组和--中等

1. 题目 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 输出&…