数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解
单链表理论知识详解
文章目录
- 单链表理论知识详解
- 1.单链表的定义
- 2.单链表的初始化
- 3.单链表的插入和删除
- 3.1 单链表的插入
- 3.1.1 按位序插入
- 3.1.2 在指定结点的前后插入
- 一.后插操作
- 二.前插操作
- 4.单链表的删除
- 4.1 按位序删除
- 4.2 指定结点的删除
- 5.单链表的查找
- 5.1 按位序查找
- 5.2 按值查找
- 补充一个:求表的长度
- 6. 单链表的建立(带头结点的建立)
- 6.1 尾插法建立单链表
- 6.2 头插法建立单链表
- 本文完整代码
1.单链表的定义
线性表的链式存储.
优点:不要求大片连续空间,改变容量方便
缺点:不可随机存取,要耗费一定空间存放指针
typedef struct LNode{int data;struct LNode *next;
}LNode, *LinkList;
typedef 取别名
将struct LNode 取别名为别的,方便书写
比如我们要声明一个该结构体的时候
由原先的struct LNode a; 可以直接写为LNode a;
由原先的struct LNode *p; 可以直接写为LinkList a;
2.单链表的初始化
带头结点的初始化,头结点就是多一个结点,指向第一个存放数据的结点.
不带头结点,会使处理数据的逻辑更复杂,对空表和非空表需要不同的代码逻辑.
单链表的初始化本质:为头结点分配一个堆空间,将头结点指针域置为空,加上判断内存是否能分配
#include <stdio.h>
#include <stdlib.h>
//这是带有头结点的单链表初始化
void InitList() {LinkList L;//定义头指针变量 L=(LNode*)malloc(sizeof(LNode));//头指针指向分配的头结点内存空间 L->next=NULL;return true;
}
int main()
{InitList( );
}
3.单链表的插入和删除
3.1 单链表的插入
3.1.1 按位序插入
按位序插入,比如说有5个元素,插入到第三个元素的位置
注意在有头结点时,位序5,意味着是结点6
假如我们要插入的位序是3,意味着我们要寻找的是位序2,也就是结点3,当j=i-1时我们跳出循环,先操作,后j++,j代表当前结点值从0开始,也就是我们在j=3的时候应该跳出循环,所以先操作,后j++,就是j<i-1,j=i的时候就跳出循环
传入什么? 表+插入位置+插入的值
分为几步?
首先是非法操作的判断,是否合法.
第二步是,寻找要插入的位置,插入第几个位置,就找到他前一个位置即i-1,让此时的指针p落在该点处,即我们可以操作他的next域
第三步,先判断吐过p指向空,插入操作不合法,若合法,分配堆空间给一个新的结点s,s的数据域是传入值e,s的指针域指向原先的i(i-1的next域,即p当前的next域),然后将i-1的next域指向新的i
核心思想:先连后断
bool ListInsert(LinkList L,int i,int e)
{if(i<1)return false;LNode *p=L; //为什么需要p指针,因为我们不能动表头指针int j=0; //用来判断当前指针在第几个结点处,j=0,意思是在头结点处while(p!=NULL&&j<i-1) //P不能为空,为空咋插入啊,操作不合法,i=j的时候跳出循环{p=p->next; j++; } //通过这个循环,我们就能找到A的指向B的next域,在他俩中间插入Cif(p==NULL)return false; //上个循环判断它是否为空,为空不执行,为空的具体操作写在了这,为空就结束LNode *s=(LNode *)malloc(sizeof(LNode));s->data=e;s->next=p->next;p->next=s;return true;
}
3.1.2 在指定结点的前后插入
一.后插操作
分两步
判断操作是否合法(p指针是否为空+s是否能分配)
插入元素操作
InsertNextNode(LNode *p,int e)
{if(p==NULL)return false;LNode *s=(LNode *)malloc(sizeof(LNode)); if(s==NULL)return false;s->data=e;s->next=p->next;p->next=s;return true;
}
二.前插操作
前插操作我们这里不讨论从前遍历一遍,到最后的那种方法
而是考虑,用后插法再交换他们的数据域这种形式可以将时间复杂度降低到o(1)
bool InsertPriorNode(LNode *p,int e)
{if(p==NULL)return false;LNode *s=(LNode *)malloc(sizeof(LNode)); if(s==NULL)return false;s->next=p->next;p->next=s;s->data=p->data;p->data=e;return true;
}
4.单链表的删除
4.1 按位序删除
第一步与之前的查找的相同的,现查找位序-1的点
然后再进行删除操作
bool ListDelete(LinkList L,int i,int &e)
{if(i<1)return false;LNode *p=L; //为什么需要p指针,因为我们不能动L头指针int j=0; //用来判断当前指针在第几个结点处,j=0,意思是在头结点处while(p!=NULL&&j<i-1) //P不能为空,为空咋插入啊,操作不合法,i=j的时候跳出循环{p=p->next; j++; } if(p==NULL) return false;if(p->next==NULL) return false;LNode *q=p->next; //方便操作搞出了一个q,直接用p也行,就是写起来不直观e=q->data;p->next=q->next;free(q);return true;
}
4.2 指定结点的删除
指定删除结点p,我们思考,给你结点p删除它,需要找到前一个结点,但是那样做太麻烦了,不如交换指定结点和后一个结点的数据域,再删除新的后继结点
bool DeleteNode(LNode *p)
{if(p==NULL)return false;LNode *q=p->next;p->data=q->data;p->next=q->next;free(q);return true;
}
5.单链表的查找
5.1 按位序查找
返回值是位序结点的指针
LNode * GetElem(LinkList L,int i)
{if(i<0)return NULL;LNode *p=L;int j=0;while(p!=NULL&&j<i){p=p->next;j++;}return p;}
5.2 按值查找
LNode * LocateElem(LinkList L,int e)
{LNode *p=L->next; //从第一个结点处开始查值while(p!=NULL&&p->data!=e){p=p->next;}return p;
}
补充一个:求表的长度
int Length(LinkList L){int len=0; //不包括头结点 LNode *p=L;while(p->next!=NULL){p=p->next;len++;}return len;
}
6. 单链表的建立(带头结点的建立)
单链表的建立包括了头结点的建立(初始化)
6.1 尾插法建立单链表
- 在尾插法中,LNode *s,*r=L;这个写法,其实是为了简化代码,实际上*s不需要赋值,
- 因为在接下来的代码中会给结点s分配堆空间,结点s的位置就会变成随机的,
- 实际上,我们只需要让r=L就行,声明一个s即可
- 声明输入值x,分配头结点,声明s和r指针
- 循环分配s结点再把它加入链表,再循环的输入x值
- 链表尾指针置空
LinkList List_Tailnsert(LinkList &L)
{int x;L=(LinkList)malloc(sizeof(LNode)); //初始化头结点LNode *s,*r=L; //定义上表尾指针和待随机分配的结点指针scanf("%d",&x);while(x!=9999) //输出9999表示结束{s=(LNode *)malloc(sizeof(LNode));s->data=x;r->next=s;r=s;scanf("%d",&x);}r->next=NULL;return L;
}
6.2 头插法建立单链表
- 头插法相比于尾插法,我们要把头指针置空,因为分配的头指针很可能指向神秘的空间有脏数据
LinkList List_Headlnsert(LinkList L)
{int x;L=(LinkList)malloc(sizeof(LNode));L->next=NULL; //初始链表头指针指向NULLLNode *s;scanf("%d",&x);while(x!=9999) //输出9999表示结束{s=(LNode *)malloc(sizeof(LNode));s->data=x;s->next=L->next;L->next=s;scanf("%d",&x);}return L;
}
本文完整代码
#include <stdio.h>
#include <stdlib.h>
typedef struct LNode{int data;struct LNode *next;
}LNode ,*LinkList;void InitList() {LinkList L;//定义头指针变量 L=(LNode*)malloc(sizeof(LNode));//头指针指向分配的头结点内存空间 L->next=NULL;return true;
}
bool ListInsert(LinkList L,int i,int e)
{if(i<1)return false;LNode *p=L; //为什么需要p指针,因为我们不能动L头指针int j=0; //用来判断当前指针在第几个结点处,j=0,意思是在头结点处while(p!=NULL&&j<i-1) //P不能为空,为空咋插入啊,操作不合法,i=j的时候跳出循环{p=p->next; j++; } if(p==NULL)return false;LNode *s=(LNode *)malloc(sizeof(LNode));s->data=e;s->next=p->next;p->next=s;return true;
}InsertNextNode(LNode *p,int e)
{if(p==NULL)return false;LNode *s=(LNode *)malloc(sizeof(LNode)); if(s==NULL)return false;s->data=e;s->next=p->next;p->next=s;return true;
}
bool InsertPriorNode(LNode *p,int e)
{if(p==NULL)return false;LNode *s=(LNode *)malloc(sizeof(LNode)); if(s==NULL)return false;s->next=p->next;p->next=s;s->data=p->data;p->data=e;return true;
}bool ListDelete(LinkList L,int i,int &e)
{if(i<1)return false;LNode *p=L; //为什么需要p指针,因为我们不能动L头指针int j=0; //用来判断当前指针在第几个结点处,j=0,意思是在头结点处while(p!=NULL&&j<i-1) //P不能为空,为空咋插入啊,操作不合法,i=j的时候跳出循环{p=p->next; j++; } if(p==NULL) return false;if(p->next==NULL) return false;LNode *q=p->next; //方便操作搞出了一个q,直接用p也行,就是写起来不直观e=q->data;p->next=q->next;free(q);return true;
}bool DeleteNode(LNode *p)
{if(p==NULL)return false;LNode *q=p->next;p->data=q->data;p->next=q->next;free(q);return true;
}LNode * GetElem(LinkList L,int i)
{if(i<0)return NULL;LNode *p=L;int j=0;while(p!=NULL&&j<i){p=p->next;j++;}return p;
}
LNode * LocateElem(LinkList L,int e)
{LNode *p=L->next; //从第一个结点处开始查值while(p!=NULL&&p->data!=e){p=p->next;}return p;
}int Length(LinkList L){int len=0; //不包括头结点 LNode *p=L;while(p->next!=NULL){p=p->next;len++;}return len;
}
LinkList List_Tailnsert(LinkList L)
{int x;L=(LinkList)malloc(sizeof(LNode));LNode *s,*r=L;scanf("%d",&x);while(x!=9999) //输出9999表示结束{s=(LNode *)malloc(sizeof(LNode));s->data=x;r->next=s;r=s;scanf("%d",&x);}r->next=NULL;return L;
}LinkList List_Headlnsert(LinkList L)
{int x;L=(LinkList)malloc(sizeof(LNode));LNode *s;scanf("%d",&x);while(x!=9999) //输出9999表示结束{s=(LNode *)malloc(sizeof(LNode));s->data=x;s->next=L->next;L->next=s;scanf("%d",&x);}return L;
}int main()
{InitList();int e=-1;
}
相关文章:
数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解
单链表理论知识详解 文章目录 单链表理论知识详解1.单链表的定义2.单链表的初始化3.单链表的插入和删除3.1 单链表的插入3.1.1 按位序插入3.1.2 在指定结点的前后插入一.后插操作二.前插操作 4.单链表的删除4.1 按位序删除4.2 指定结点的删除 5.单链表的查找5.1 按位序查找5.2 …...

【智能时代的创新工具】LangChain快速入门指南:轻松掌握语言模型的集成与运用
一、LangChain:连接语言模型与现实世界的桥梁 1.1 LangChain的定义与重要性 LangChain是一个开源的Python库,它旨在为开发人员提供一种简便的方式来集成和运用语言模型。它不仅仅是一个简单的API调用工具,而是一个具有丰富功能的框架&#x…...

文献阅读:细胞分辨率全脑图谱的交互式框架
文献介绍 文献题目: An interactive framework for whole-brain maps at cellular resolution 研究团队: Daniel Frth(瑞典卡罗林斯卡学院)、Konstantinos Meletis(瑞典卡罗林斯卡学院) 发表时间ÿ…...

YAML基础语言深度解析
引言 YAML(YAML Aint Markup Language,即YAML不是一种标记语言)是一种直观、易于阅读的数据序列化格式,常用于配置文件、数据交换和程序间的通信。其设计目标是易于人类阅读和编写,同时也便于机器解析和生成。在本文中…...

xcode使用
1. 界面 1.1. Build Settings,Build Phases和Build Rules三个设置项 Build Settings(编译设置): 每个选项由标题(Title)和定义(Definition)组成。这里主要定义了Xcode在编译项目时的一些具体配置 Build Phases(编译资源):用于指定编译过程中项目所链接的原文件,依赖对象,库…...

OV2640引脚的定义(OV2640 FPC模组规格书(接口线序))
OV2640是一款由Omni Vision公司生产的1/4寸CMOS UXGA(1632x1222)图像传感器。这款传感器以其小巧的体积、低工作电压和强大的功能而著称,它集成了单片UXGA摄像头和影像处理器,能够通过SCCB总线控制输出各种分辨率的8/10位影像数据…...

CTFSHOW 萌新 web10 解题思路和方法(passthru执行命令)
点击题目链接,分析页面代码。发现代码中过滤了system、exec 函数,这意味着我们不能通过system(cmd命令)、exec(cmd命令)的方式运行命令。 在命令执行中,常用的命令执行函数有: system(cmd_code);exec(cmd_…...
深入Java数据库连接和JDBC
引言 Java数据库连接(JDBC)是Java语言中用于执行SQL语句的标准API。通过JDBC,开发者可以方便地与关系型数据库进行交互。然而,直接使用JDBC API面临着数据库连接管理复杂、性能瓶颈等问题。数据库连接池作为一种解决方案,可以有效地管理数据库连接,提高应用程序的性能。…...
灰狼优化算法(GWO)与长短期记忆网络(LSTM)结合的预测模型(GWO-LSTM)及其Python和MATLAB实现
#### 一、背景 在现代数据科学和人工智能领域,预测模型的准确性和效率是研究者和工程师不断追求的目标,尤其是在时间序列预测、金融市场分析、气象预测等领域。长短期记忆(LSTM)网络是一种解决传统递归神经网络(RNN&a…...

电路板热仿真覆铜率,功率,结温,热阻率信息计算获取方法总结
🏡《电子元器件学习目录》 目录 1,概述2,覆铜率3,功率4,器件尺寸5,结温6,热阻1,概述 电路板热仿真操作是一个复杂且细致的过程,旨在评估和优化电路板内部的热分布及温度变化,以确保电子元件的可靠性和性能。本文简述在进行电路板的热仿真时,元器件热信息的计算方法…...
C#中多线程编程中的同步、异步、串行、并行及并发及死锁
在C#中,多线程编程是一个强大的功能,它允许程序同时执行多个任务。然而,这也带来了复杂性,特别是在处理同步、异步、串行、并行、并发以及死锁等问题时。下面我将详细解释这些概念,并给出一些C#中的示例和注意事项。 …...

【Lampiao靶场渗透】
文章目录 一、IP地址获取 二、信息收集 三、破解SSH密码 四、漏洞利用 五、提权 一、IP地址获取 netdiscover -i eth0 Arp-scan -l Nmap -sP 192.168.78.0/24 靶机地址:192.168.78.177 Kali地址:192.168.78.128 二、信息收集 nmap -sV -p- 192.…...

使用WebSocket实现log日志流的实时展示-从轮询到通知
场景介绍 最近开发一个系统,其中一个模块需要展示实时的执行过程,过程日志可能比较多。以前的方案都是前端定时轮询,比如每秒查一次后端接口,将拉取回来的日志重新展示。轮询方案简单容易实现,但是比较消耗资源&#…...

UE5 从零开始制作跟随的大鹅
文章目录 二、绑定骨骼三、创建 ControlRig四、创建动画五、创建动画蓝图六、自动寻路七、生成 goose八、碰撞 和 Physics Asset缺点 # 一、下载模型 首先我们需要下载一个静态网格体,这里我们可以从 Sketchfab 中下载:Goose Low Poly - Download Free …...

O’Reilly
--江上往来人,但爱鲈鱼美。 --君看一叶舟,出没风波里。 OReilly OReilly出版社出版的技术类图书 俗称动物系列 应该是每个技术人员的必备手册。 OReilly动物系列(中译本) 简介" 动物系列作为 OReilly 书籍的典型代表被普遍…...

优盘驱动器未格式化:数据拯救行动指南
优盘困境:驱动器未格式化的挑战 在日常的数据存储与传输中,优盘以其便携性和高容量成为了我们不可或缺的伙伴。然而,当您尝试访问优盘时,突然弹出的“驱动器未被格式化”提示却如同晴天霹雳,让人措手不及。这一状况不…...
4.Handler mappings
处理程序映射 简介 在早期版本的 Spring 中,用户需要在 Web 应用程序上下文中定义一个或多个 HandlerMapping bean 以将传入的 Web 请求映射到适当的处理程序。随着注解控制器的引入,通常不再需要这样做,因为 RequestMappingHandlerMapping…...

《学会 SpringMVC 系列 · 消息转换器 MessageConverters》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...

深度学习项目 -7-使用 Python 的手写数字识别
一、前言 该文章仅作为个人学习使用 二、正文 项目源代码:深度学习项目 - 使用 Python 进行手写数字识别 - DataFlair (data-flair.training) 数据集:https://drive.google.com/open?id1hJiOlxctFH3uL2yTqXU_1f6c0zLr8V_K Python 深…...

MySQL —— 库,数据类型 与 表
库与基础操作 1.1 查看数据库 使用 show databases; 可以查看当前 MySQL 目前有多少个数据库 5 rows 表示有 5 行,这里是表示的是有效的数据,不包括 第一行的指引 set 表示结果集合 0.01 sec 表示这个 sql 语句一共运行了0.01 秒,一般情况…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...

ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...