链表|数据结构|C语言深入学习

什么是链表

离散,就是“分离的、散开的”

链表是什么样子的:

有限个节点离散分配

彼此间通过指针相连

除了首尾节点,每个节点都只有一个前驱节点和一个后继节点

首节点没有前驱结点,尾节点没有后继节点

基本概念术语:

首节点:第一个存放有效数据的节点;

尾节点:最后一个存放有效数据的节点

头节点是首节点前面的那个节点。头结点里面不存放数据,有效数据是从首节点开始存的

头结点存在的目的是什么?

对链表进行操作的时候,在前面加上一个没有实际含义的头节点可以方便对链表进行操作

头指针:指向头节点的指针变量,存放了头节点的地址

尾指针:指向尾节点的指针变量,存放了尾节点的地址

链表中所有的节点的数据类型都是一样的,包括头结点

确定一个链表最少需要几个参数

只需要一个参数:头指针

通过头指针可以得到一个链表的其他所有信息

链表节点的数据类型怎么表示

每一个节点都有数据域指针域两部分

尾节点除外的每一个节点的指针域指向了下一个节点的[节点整体]

这个[节点整体]的数据类型跟他的前一个节点的数据类型是完全一样的

也就是说,一个结构体变量中的某一成员指向了跟它整体(这个结构体变量)一样的数据类型

因为第一个节点的指针域指向了第二个节点整体,而第二个节点整体和第一个节点整体的数据类型是一模一样的

说的抽象一点,就是:某一结构体的变量中的一个成员指向了和它本身的数据类型一样的另一个变量

typedef struct Node {

int data;//数据域

struct Node* pNext;//指针域  

}NODE,*PNODE;

//NODE是struct Node类型,PNODE是struct Node*类型

/*

指针域指向了和它整体本身的数据类型一模一样的另一个变量的整体

指针域存放的是下一个节点的地址,所以应该是【下一个节点的数据类型*】 类型的指针变量

下一个节点跟这个节点本身的数据类型一样,都是struct Node类型

所以,指针域的指针变量的类型是struct Node*类型

*/

链表的分类

·单链表

·双链表

每一个节点都有两个指针域

·循环链表

能通过任何一个节点找到其他所有的节点

·非循环链表

非循环单链表的创建和遍历

#include<stdio.h>

#include<malloc.h>

#include<stdlib.h>

typedef struct Node {

int data;//数据域

struct Node* pNext;//指针域  

}NODE, * PNODE;

//NODE是struct Node类型,PNODE是struct Node*类型

/*

  指针域指向了和它整体本身的数据类型一模一样的另一个变量的整体

  指针域存放的是下一个节点的地址,所以应该是【下一个节点的数据类型*】 类型的指针变量

  下一个节点跟这个节点本身的数据类型一样,都是struct Node类型

  所以,指针域的指针变量的类型是struct Node*类型

 */

PNODE createList();

void traverseList(PNODE pHead);

int main() {

PNODE pHead = NULL;

//等价于struct Node* PHead = NULL;

pHead = createList();//创建一个非循环单链表,并将链表的头结点的地址赋给pHead

traverseList(pHead);

return 0;

}

PNODE createList() {

int len;//用来存放链表有效节点的个数

int val;//用来临时存放用户输入的节点的个数

/*

  pHead是一个头指针,pHead指向了头结点

  这个头结点不存放有效数据*/

PNODE pHead = (PNODE)malloc(sizeof(NODE));

if (pHead == NULL) {

printf("内存分配失败,程序终止\n");

exit(-1);

}

PNODE pTail = pHead;

pTail->pNext = NULL;

printf("请输入需要生成的链表节点的个数:");

scanf("%d", &len);

for (int i = 0; i < len; i++) {

printf("请输入需要生成的链表节点值:");

scanf("%d", &val);

PNODE pNew = (PNODE)malloc(sizeof(NODE));

if (pHead == NULL) {

printf("内存分配失败,程序终止\n");

exit(-1);

}

pNew->data = val;

pTail->pNext = pNew;

pNew->pNext = NULL;

pTail = pNew;

}

/*

  循环了len次,每一次都创建并分配了一个新节点,用pNew(指针变量)表示这个新节点

  创建了新节点后,为新节点数据域赋值

  创建了一个pTail指针变量,链表中没有有效节点之前pTail指向头结点(此时,头指针pHead和pTail都指向头结点)

  创建了一个新节点之后,为新节点赋值数据域,pNew->data = val

  pTail指针变量指向的节点(最开始是头结点,之后是链表中最后一个节点)的指针域存上新节点的地址pTail->pNext = pNew

  此时pTail指针变量指向的是倒数第二个节点

  将新茶入的节点的指针域置空pNew->pNext = NULL

  将pTail指针变量指到最后一个新插入的节点上,确保下一次插入新节点之前pTail总是指到当前链表最后一个节点上

  pTail = pNew*/

return pHead;

}

void traverseList(PNODE pHead) {

PNODE p = pHead->pNext;

while (NULL != p) {

printf("%d ", p->data);

p = p->pNext;

}

printf("\n");

}

非循环单链表插入节点

非循环单链表删除节点

链表排序算法

链表冒泡排序:

void sortList(PNODE pHead) {

if (isEmpty(pHead)) {

printf("链表为空,无法排序");

exit(-1);

}

PNODE p, q;

int i, j;

int n = numNode(pHead);//求链表有效节点个数

for (i = 0, p = pHead->pNext; i < n - 1; i++, p = p->pNext) {

for (j = 0, q = pHead->pNext; j < n - 1 - i; j++, q = q->pNext) {

if (q->data > q->pNext->data) {

int t = q->data;

q->data = q->pNext->data;

q->pNext->data = t;

}

}

}

}

由以下数组冒泡排序改编:

//数组冒泡排序:

for (int i = 0; i < n - 1; i++) {

for (int j = 0; j < n - 1 - i; j++) {

if (a[j] < a[j + 1]) {

int t = a[j];

a[j] = a[j + 1];

a[j + 1] = t;

}

}

}

链表完整代码

#include<stdio.h>

#include<malloc.h>

#include<stdlib.h>

typedef struct Node {

int data;//数据域

struct Node* pNext;//指针域  

}NODE, * PNODE;

//NODE是struct Node类型,PNODE是struct Node*类型

/*

  指针域指向了和它整体本身的数据类型一模一样的另一个变量的整体

  指针域存放的是下一个节点的地址,所以应该是【下一个节点的数据类型*】 类型的指针变量

  下一个节点跟这个节点本身的数据类型一样,都是struct Node类型

  所以,指针域的指针变量的类型是struct Node*类型

 */

PNODE createList();//创建

void traverseList(PNODE pHead);//遍历

bool isEmpty(PNODE pHead);//判空

int numNode(PNODE pHead);//求链表有效节点个数

void sortList(PNODE pHead);//排序

bool insertNode(PNODE pHead, int pos, int val);//插入节点

bool deleteNode(PNODE pHead, int pos);//删除节点

int main() {

PNODE pHead = NULL;

//等价于struct Node* PHead = NULL;

pHead = createList();//创建一个非循环单链表,并将链表的头结点的地址赋给pHead

traverseList(pHead);//遍历

if (isEmpty(pHead))

printf("链表为空");

else

printf("链表非空");

//求链表有效节点的个数

printf("链表中有效节点的个数为%d\n", numNode(pHead));

sortList(pHead);

traverseList(pHead);//遍历

insertNode(pHead, 1, 125);

traverseList(pHead);//遍历

deleteNode(pHead, 1);

traverseList(pHead);//遍历

return 0;

}

PNODE createList() {

int len;//用来存放链表有效节点的个数

int val;//用来临时存放用户输入的节点的个数

/*

  pHead是一个头指针,pHead指向了头结点

  这个头结点不存放有效数据*/

PNODE pHead = (PNODE)malloc(sizeof(NODE));

if (pHead == NULL) {

printf("内存分配失败,程序终止\n");

exit(-1);

}

PNODE pTail = pHead;

pTail->pNext = NULL;

printf("请输入需要生成的链表节点的个数:");

scanf("%d", &len);

for (int i = 0; i < len; i++) {

printf("请输入需要生成的链表节点值:");

scanf("%d", &val);

PNODE pNew = (PNODE)malloc(sizeof(NODE));

if (pHead == NULL) {

printf("内存分配失败,程序终止\n");

exit(-1);

}

pNew->data = val;

pTail->pNext = pNew;

pNew->pNext = NULL;

pTail = pNew;

}

/*

  循环了len次,每一次都创建并分配了一个新节点,用pNew(指针变量)表示这个新节点

  创建了新节点后,为新节点数据域赋值

  创建了一个pTail指针变量,链表中没有有效节点之前pTail指向头结点(此时,头指针pHead和pTail都指向头结点)

  创建了一个新节点之后,为新节点赋值数据域,pNew->data = val

  pTail指针变量指向的节点(最开始是头结点,之后是链表中最后一个节点)的指针域存上新节点的地址pTail->pNext = pNew

  此时pTail指针变量指向的是倒数第二个节点

  将新茶入的节点的指针域置空pNew->pNext = NULL

  将pTail指针变量指到最后一个新插入的节点上,确保下一次插入新节点之前pTail总是指到当前链表最后一个节点上

  pTail = pNew*/

return pHead;

}

void traverseList(PNODE pHead) {

PNODE p = pHead->pNext;

while (NULL != p) {

printf("%d ", p->data);

p = p->pNext;

}

printf("\n");

}

bool isEmpty(PNODE pHead) {

if (pHead->pNext == NULL)

return true;

else

return false;

}

int numNode(PNODE pHead) {

PNODE p = pHead->pNext;

int num = 0;

while (p != NULL) {

num++;

p = p->pNext;

}

return num;

}

void sortList(PNODE pHead) {

if (isEmpty(pHead)) {

printf("链表为空,无法排序");

exit(-1);

}

PNODE p, q;

int i, j;

int n = numNode(pHead);//求链表有效节点个数

for (i = 0, p = pHead->pNext; i < n - 1; i++, p = p->pNext) {

for (j = 0, q = pHead->pNext; j < n - 1 - i; j++, q = q->pNext) {

if (q->data > q->pNext->data) {

int t = q->data;

q->data = q->pNext->data;

q->pNext->data = t;

}

}

}

}

bool insertNode(PNODE pHead, int pos, int val) {

PNODE p = pHead;

int i = 0;

while (p && i < pos) {

p = p->pNext;

i++;

}

if (p == NULL || i > pos) {

return false;

}

PNODE pNew = (PNODE)malloc(sizeof(NODE));

if (pNew == NULL) {

printf("动态内存分配失败");

return false;

}

pNew->data = val;

pNew->pNext = p->pNext;

p->pNext = pNew;

return true;

}

bool deleteNode(PNODE pHead, int pos) {

int i = 0;

PNODE p = pHead;

while (p && i < pos) {

p = p->pNext;

i++;

}

if (!p || i > pos)

return false;

PNODE q = (PNODE)malloc(sizeof(NODE));

q = p->pNext;

p->pNext = p->pNext->pNext;

free(q);

return true;

}

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

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

相关文章

C 练习实例38

题目&#xff1a;求一个3*3矩阵对角线元素之和 程序分析&#xff1a;利用双重for循环控制输入二维数组&#xff0c;再将a[i][i]累加后输出。 代码&#xff1a; #include <stdio.h> int main() {int a[3][3];int i; //行序列int j; //列序列int k; //对角线序列int cnt…

1.使用分布式文件系统Minio管理文件

分布式文件系统DFS分类 文件系统 文件系统是操作系统用于组织管理存储设备(磁盘)或分区上文件信息的方法和数据结构,负责对文件存储设备空间进行组织和分配,并对存入文件进行保护和检索 文件系统是负责管理和存储文件的系统软件&#xff0c;操作系统通过文件系统提供的接口去…

Docker五部曲之五:通过Docker和GitHub Action搭建个人CICD项目

文章目录 项目介绍Dockerfile解析compose.yml解析Nginx反向代理到容器以及SSL证书设置MySQL的准备工作Spring和环境变量的交互 GitHub Action解析项目测试结语 项目介绍 该项目是一个入门CICD-Demo&#xff0c;它由以下几部分组成&#xff1a; Dockerfile&#xff1a;用于构建…

「 典型安全漏洞系列 」05.XML外部实体注入XXE详解

1. XXE简介 XXE&#xff08;XML external entity injection&#xff0c;XML外部实体注入&#xff09;是一种web安全漏洞&#xff0c;允许攻击者干扰应用程序对XML数据的处理。它通常允许攻击者查看应用程序服务器文件系统上的文件&#xff0c;并与应用程序本身可以访问的任何后…

【ARM 嵌入式 编译系列 3.6 -- 删除lib中的某个文件】

请阅读【嵌入式开发学习必备专栏 之 ARM GCC 编译专栏】 文章目录 删除lib中的某个文件 删除lib中的某个文件 比如&#xff0c;如果要删除 libc.a 静态库中的特定对象文件并重新使用这个静态库&#xff0c;在终端中可以使用 ar 命令。ar 是一个归档工具&#xff0c;它可以创建…

【Unity小技巧】3D人物移动脚步和跳跃下落音效控制

文章目录 单脚步声多脚步声&#xff0c;跳跃落地音效播放不同材质的多脚步声完结 单脚步声 public AudioClip walkingSound; public AudioClip runningSound;//移动音效 public void MoveSound() {// 如果在地面上并且移动长度大于0.9if (isGround && moveDirection.s…

动物免疫(羊驼免疫)-泰克生物

在过去几十年里&#xff0c;抗体研究和应用的领域已经经历了革命性的变化。在这个进程中&#xff0c;一种特殊来源的抗体 —— 来自骆驼科动物&#xff08;包括羊驼&#xff09;的单链抗体&#xff08;也称纳米抗体&#xff09;引起了全球科学家的广泛关注。 羊驼等骆驼科动物…

Java NIO (四)NIO Selector类

1 选择器与注册 选择器是什么&#xff1f;选择器和通道关系是什么&#xff1f; 简单的说&#xff0c;选择器的使用是完成IO的多路复用&#xff0c;其主要工作是通道的注册、监听、事件查询。一个通道代表一条连接通路&#xff0c;通过选择器可以同时监听多个通道的IO(输入输出)…

长拖尾数据的采样方法

以下内容来自于ChatGPT长拖尾数据的采样方式&#xff1a; 对于具有长拖尾&#xff08;长尾&#xff09;分布的数据&#xff0c;通常使用传统的随机抽样方法可能不太适用&#xff0c;因为这样的分布意味着有一些极端值&#xff08;outliers&#xff09;会对整体分布产生较大影响…

【Linux】python版本控制和环境管理

文章目录 1.查看目前python的版本2.添加软件源并更新3.选择你想要下载的版本4.警示&#xff1a;没必要设置默认版本误区千万千万不要覆盖python3软链接解决办法 5.pip软件包管理最省心稍微麻烦换源 网上有很多教程都是教导小白去官方下载之后编译安装。但是&#xff0c;小白连c…

4.go 基础类型及类型转换

目录 概述basic types总结例子 Zero values总结例子 类型转换总结例子 结束 概述 go 版本&#xff1a; go1.20.13 basic types 总结 基本类型如下&#xff1a; boolstringint int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptrbyte // alias for uint8…

[pytorch入门] 2. tensorboard

tensorboard简介 TensorBoard 是一组用于数据可视化的工具。它包含在流行的开源机器学习库 Tensorflow 中.但是也可以独立安装&#xff0c;服务Pytorch等其他的框架 可以常常用来观察训练过程中每一阶段如何输出的 安装pip install tensorboard启动tensorboard --logdir<d…

蓝桥杯理历年真题 —— 数学

1. 买不到的数目 这道题目&#xff0c;考得就是一个日常数学的积累&#xff0c;如果你学过这个公式的话&#xff0c;就是一道非常简单的输出问题&#xff1b;可是如果没学过&#xff0c;就非常吃亏&#xff0c;在考场上只能暴力求解&#xff0c;或是寻找规律。这就要求我们什么…

Python图像处理【19】基于霍夫变换的目标检测

基于霍夫变换的目标检测 0. 前言1. 使用圆形霍夫变换统计图像中圆形对象2. 使用渐进概率霍夫变换检测直线2.1 渐进霍夫变换原理2.2 直线检测 3. 使用广义霍夫变换检测任意形状的对象3.1 广义霍夫变换原理3.2 检测自定义形状 小结系列链接 0. 前言 霍夫变换 (Hough Transform,…

H5112C PWM调光 无频闪 高性价比 支持12V 24V 36V 48V 60V 72V 内置MOS

PWM调光芯片是一种常用于LED调光控制的芯片&#xff0c;其工作原理如下&#xff1a; 脉冲宽度调制&#xff08;PWM&#xff09;&#xff1a;PWM是一种调制技术&#xff0c;通过改变信号的脉冲宽度来控制输出信号的平均功率。在PWM调光中&#xff0c;芯片会以一定的频率产生一系…

SpringCloud Alibaba 深入源码 - Nacos 和 Eureka 的区别(健康检测、服务的拉取和订阅)

目录 一、Nacos 和 Eureka 的区别 1.1、以 Nacos 注册流程来解析区别 一、Nacos 和 Eureka 的区别 1.1、以 Nacos 注册流程来解析区别 a&#xff09;首先&#xff0c;我们的服务启动时。都会把自己的信息提交给注册中心&#xff0c;然后注册中心就会把信息保存下来. 注册的…

Midjourney常见参数列表(极速版)

前言 参数是添加到提示词末尾的选项&#xff0c;可以改变图片的生成方式。参数可以改变图片的长宽比&#xff08;Aspect Ratios&#xff09;&#xff0c;切换不同的Midjourney模型版本&#xff08;Model Versions&#xff09;&#xff0c;改变使用的放大器&#xff08;Upscaler…

Leetcode 3016. Minimum Number of Pushes to Type Word II

Leetcode 3016. Minimum Number of Pushes to Type Word II 1. 解题思路2. 代码实现 题目链接&#xff1a;3016. Minimum Number of Pushes to Type Word II 1. 解题思路 这道题的话思路其实还是蛮简单的&#xff0c;显然我们的目的是要令对给定的word在键盘上敲击的次数最小…

ELK日志分析

目录 一、ELK概述 &#xff08;一&#xff09;ELK的定义 &#xff08;二&#xff09;ELK工具 1.ElasticSearch 2.Kiabana 3.Logstash &#xff08;1&#xff09;定义 &#xff08;2&#xff09;插件 ① input ② filter ③ output &#xff08;三&#xff09;可以添…

Spring、Spring-MVC、Mybatis、Mybatis-generator整合核心配置文件记录

Spring、Spring-MVC、Mybatis、Mybatis-generator整合核心配置文件记录 文章目录 Spring、Spring-MVC、Mybatis、Mybatis-generator整合核心配置文件记录1. spring.xml2. spring-mvc.xml3. mybatis-config.xml4. mybaits-generator.xml5. ehcach.xml6. web.xml Spring、Spring-…