Linked List

news2024/9/25 21:28:45

文章目录

  • 链表
    • 定义
    • 专业术语
    • 代码
    • 链表分类
    • 常见算法
    • 链表创建和常用算法
  • 链表总结

链表

补充知识
typedef
给类型换名字,比如

typedef struct Student
{
	int sid;
	char name[100];
	char sex;
}ST;//ST就代表了struct Student
//即这上方一大坨都可以用ST表示
//原先结构体定义对象是通过下面这种方式实现的
struct Student st;
//现在使用typedef后,即可用下方方式定义
ST st;

或者来一个结构体指针定义。

typedef struct Student
{
	int sid;
	char name[100];
	char sex;
}*PST;
//这样PST等价于struct Student *
//这样初始化后就可以直接初始化一个结构体指针
PST ps = &st;
//之后ps进行指针的调用就行,例如下所示
ps->sid = 99;

离散存储

离散的含义,任何一个点到其他点之间是有间距的。

定义

n个节点离散分配,彼此通过指针相连接,每个节点只有一个前驱节点,每个节点只有一个后继节点,首节点没有前驱节点,尾节点没有后继节点。

专业术语

  • 首节点:第一个有效的节点。
  • 尾节点:最后一个有效节点。
  • 头结点:在首节点的前面加上这个结点,即第一个有效节点前的节点叫做头结点。头结点里面没有存放有效数据,也没有存放链表有效节点的个数,其真正含义是可以方便我们对链表进行操作(增删改查)。
  • 头指针:指向头结点的指针变量(存放了头结点的地址)。
  • 尾指针:指向尾节点的指针变量。

确定一个链表需要几个参数?
首节点可以通过头结点推出来,所以不是必须的,尾指针是0,因为没有后继节点,所以尾指针也不是必须的。尾节点也不是必须的,找到最后空的就知道尾节点,所以也不是必须的。头指针包含了指向头结点的地址,所以头结点也不是必须的,记下头指针就行。首节点可以由头结点推出来,所以首节点也不是必须的。所以综上,只要知道头结点的地址,就可以把整个链表的所有信息都找到。所以说确定一个链表只需要一个参数,即为头指针。
头结点的数据类型和首节点的数据类型是一样的。

代码

通过代码来模拟链表。
每一个节点的数据类型都是一模一样的,一个节点可以分成两部分,一部分是存放有效的数据,另有一部分是存放指针,指向后面的一个节点。这样就造成了每一个节点的数据类型是一样的。这里面的指针指向的是第二个节点的整体。
在这里插入图片描述
所以现在是包含了一个数据域一个指针域,

typedef struct Node{
	int data;//数据域
	struct Node *pNext;//指针域 
	//这里的指针域指向的是与其数据类型一致,但是另外一个节点。(下一节点的地址)(本节点的指针指向了下一节点)
}NODE, *PNODE;
//NODE是struct Node类型,*PNODE是struct Node *类型,记住struct Node是包含了整个整体的,要带上花括号{},即struct Node{}

链表分类

  • 单向链表
  • 双向链表:这下相比单链表,每个节点分成了三个部分,分别有指向自己的前驱和后继的指针,以及存放有效值。
  • 循环链表:能通过任何一个节点找到其他所有的节点,就是首尾节点连接了。
  • 非循环链表

常见算法

  1. 遍历
  2. 查找
  3. 清空
  4. 销毁
  5. 求长度
  6. 排序
  7. 删除节点
  8. 插入节点

狭义的算法是与数据的存储方式密切相关的,广义的算法是与数据的存储方式无关。泛型是利用某种技术达到的效果就是,不同的存存储方式,执行的操作时是一样的。(泛型是一种假象)

插入节点伪代码:

r = p->next;
p->next = q;
q->next = r;
//
q->next = p->next;
p->next = q;
//以上两种方法都能实现q节点插入到p和p.next中间

删除节点的伪代码:

r = p->next;
p->next = p-next->next;
free(r);
//C当中不会自动释放内存,所以得手动释放内存,free
//C++当中是delete

链表创建和常用算法

#include <iostream>
#include <cmalloc>

using namespace std;

typedef struct Node{
	int data;//数据域
	struct Node *pNext;//指针域
}Node, *PNODE;
//现在就是定义了这么一个数据类型,叫做struct Node。

//函数声明
PNODE create_list(void);
void traverse_list(PNODE pHead);//遍历
bool is_empty(PNODE pHead);//判断是否为空,就看pHead->pNext == nullptr的结果
int length_list(PNODE);//链表长度
bool insert_list(PNODE, int, int);
bool delete_list(PNODE, int, int*);
//可以把删除的结点放到第三个参数当中去,也就是delete删除的元素可以暂存到第三个参数当中去。delete_list(pHead, 3, &val);
void sort_list(PNODE);//排序

int main(void){
	PNODE pHead = nullptr;
	//等价于struct Node *pHead = nullptr;
	pHead = create_list();
	//create_list()函数的功能就是创建一个非循环单向链表,然后把单向链表的首地址赋给pHead。
	//创建一个非循环单向链表,并将该链表的头结点地址,赋给pHead。
	sort_list(pHead);
	traverse_list(pHead);
	//insert_list(pHead, 4, 33);
	int len = length_list(pHead);
	cout << "链表长度是: " << endl;
	//这是代表遍历的意思,之前也说了,推出链表的所有参数只需要一个头结点指针(头指针)就行。
	if (is_empty(pHead))
		cout << "链表为空" << "\n" << endl;
	else
		cout << "链表非空" << "\n" << endl;
	return 0;
}
//因为动态内存管理,在一个函数当中申请的内存可以在另外一个函数当中调用。

//创建函数
PNODE create_list(void)//最后只要分配好的内存地址就行
{
	int len;//存放有效节点的个数
	int val;//临时存放用户输入的结点的值
	//分配了一个不存放数据的头结点
	PNODE pHead = (PNODE)malloc(sizeof(NODE));
	if (pHead == nullptr)
	{
		cout << "分配失败,程序终止" << endl;
		return -1;
	}
	PNODE pTail = pHead;
	pTail->pNext = nullptr;//这样永远指向尾节点
	
	cout << "请输入您需要生产的链表节点的个数:len = " << endl;
	cin >> len;
	for(int i = 0; i < len; ++i)
	{
		cout << "请输入第 " << i+1 << " 个节点的值: " << endl;//i+1是因为链表从1开始的,这里的i是从0开始的
		cin >> val;
		//每循环一次,就用pNew造出一个新的节点
		PNODE pNew = (PNODE)malloc(sizeof(NODE));//临时节点
		if (pNew == nullptr)
		{
			cout << "分配失败,程序终止" << endl;
			return -1;
		}
		//总而言之就是利用pHead生成一个临时节点,然后把数值放到临时节点的数据域当中去,再把临时节点挂到之前一个节点的后面,最后再把临时节点清空。但是这样有问题,每次新生成的结点都会挂到之前一个节点的后面,造成“一对多”的现象,所以解决方法就是,每次新生成的结点都要挂到整个链表的尾节点的后面。所以定义一个pTail永远指向尾节点。
		pNew->data = val;
		pTail->pNext = pNew;
		pNew->pNext = nullptr;
		pTail = pNew;
	}
	return pHead;//返回头结点地址
}

//遍历函数
//主要思路,先定义一个指针p,指向第一个有效的结点,如果此时指向的结点不为空,就把数据域给输出就行,再往后移一个。
void travese_list(PNODE pHead)
{
	PNONDE p = pHead->pNext;
	while(p != nullptr)
	{
		cout << p_data << endl;
		p = p->pNext;//一定要往后移,往后移才能指向下一个
	}
	cout << "\n" << "输出完毕" << endl;
	return;
}

//判断是否为空的函数
bool is_empty(PNODE pHead)
{
	if(pHead->pNext == nullptr)
		return true;
	else
		return false;
}

//长度函数
int length_list(PNODE pHead)
{
	PNODE p = pHead->pNext;
	int cnt = 0;
	while(p->pNext != nullptr)
	{
		++cnt;
		p = p->pNext;
	}
	return cnt;
}

//排序函数
//依次把数每次和后面的数进行比较,这下就是升序排序的
void sort_list(PNODE pHead)
{
	int i, j, t;
	int len = length_list(pHead);
	PNODE p, q;
	for (i = 0 ,p = pHead->pNext; i<len-1; ++i, p = p->pNext)
	{
		for (j = j+1, q = p->pNext; j<len; ++j, q = q->pNext)
		{
		if (p->data > q->data) //类似于数组中的a[i]>a[j]
		{
			t = p->data;//t = a[i];
			p->data = q->data;//a[i] = a[j];
			q->data = t;//a[j] = t;
		  }
	  }
  }
	return;
}
//听完郝老师讲的这里,我才真正知道在C++当中函数重载的具体意思,operator之类的醍醐灌顶。

//插入函数
//在pHead所指向的链表的第pos个节点的前面插入一个新的节点,该节点的值是val,并且pos的值是从1开始。记住pos不包含头结点,而是从首节点(有效节点)开始。
bool insert_list(PNODE pHead, int pos, int val)
{
	int i = 0;
	PNODE p = pHead;
	while(p != nullptr && i < pos-1)
	//这里的p是代表不是最后一个,i<pos-1表示找到插入位置之前的结点。
	//while函数的作用是将p移动到pos-1的位置,画图就好理解。
	{
		p = p->pNext;
		++i;
	}
	if (i > pos-1 || p == nullptr)
	//这里的if是判断要插入的位置是否超出了链表多一个位置
	//i>pos-1是判断pos是否为小于1的数,若是则直接false
	//p = nullptr是为了处理插入的位置,例如有5个节点,现在在第7个节点位置插入。因为这是接着while循环的,所以多一层if判断经过while循环后的p的变化,同时还能判断是否为空链表的存在。
		return false;
	PNODE pNew = (PNODE)malloc(sizeof(NODE));
	if (pNew == nullptr)
	{
		cout << "动态内存分配失败" << "\n" << endl;
		return -1;
	}
	//以上pos等于1的时候,不执行前面两个表达式,即while和if,直接执行后面的,此时将新元素插入到头结点和第一个有效节点之间。
	pNew->data = val;
	PNODE q = p->pNext;
	p->pNext = pNew;
	pNew->pNext = q;
	return true;
}

//删除函数
bool delete_list(PNODE p, int pos, int*pVal)
{
	int i = 0;
	PNODE p = pHead;
	while(p->pNext != nullptr && i < pos-1)
	{
		p = p->pNext;
		++i;
	}
	if (i > pos-1 || p->pNext == nullptr)
		return false;
	PNODE q = p->pNext;
	*pVal = q->data;
	
	//删除p节点后面的结点
	p->pNext = p->pNext->pNext;
	free(q);
	q = nullptr;
	return true;
}

链表总结

狭义的讲:数据结构是专门研究数据存储的问题,数据的存储包含两方面,个体的存储,以及个体关系的存储。算法是对存储数据的操作。
广义的讲:数据结构既包含数据的存储也包含数据的操作,对存储数据的操作就是算法。

算法:
狭义的讲:算法是和数据的存储方式密切相关。
广义的讲:算法和数据的存储方式无关。
这就是泛型的思想。

数据的存储结构有几种:
线性:连续存储【数组】,离散存储【链表】,线性结构的应用—栈,队列。

链表的优缺点:

  1. 插入和删除快
  2. 存取元素速度慢
  3. 存储容量无限

数组的优缺点:

  1. 存取速度很快
  2. 但事先必须知道数组的长度
  3. 插入删除元素很慢
  4. 空间通常是有限制的
  5. 需要大块连续的内存块

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

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

相关文章

20.3 HTML表格

1. table表格 table标签是HTML中用来创建表格的元素. table标签通常包含以下子标签: - th标签: 表示表格的表头单元格(table header), 用于描述列的标题. - tr标签: 表示表格的行(table row). - td标签: 表示表格的单元格(table data), 通常位于tr标签内, 用于放置单元格中的…

AAOS 音频焦点请求

文章目录 前言基本概念提供给应用来获取音频焦点的apiAAOS中的音频焦点管理交互矩阵duck的实现流程AAOS 测试应用kitchensink焦点相关 前言 本文章的目标是首先了解Android中音频焦点的基本概念&#xff0c;理解代码中相关音频焦点的使用方法。其次理解AAOS 中相关交互矩阵概念…

数据结构——无头单向非循环链表

无头单向非循环链表的建立 前言——什么链表链表形象图链表分类 一、Single_linked_list.h头文件的建立二、Single_linked_list.c功能函数的定义Single_linked_list_test.c主函数的定义四、代码运行测试五、Single_linked_list完整代码演示&#xff1a;总结 前言——什么链表 链…

【Docker】容器的数据卷

目录 一、数据卷的概念与作用 二、数据卷的配置 三、数据卷容器的配置 一、数据卷的概念与作用 在了解什么是数据卷之前我们先来思考以下这些问题&#xff1a; 1.如果我们一个容器在使用后被删除&#xff0c;那么他里面的数据是否也会丢失呢&#xff1f;比如容器内的MySQL的…

18.Netty源码之ByteBuf 详解

highlight: arduino-light ByteBuf 是 Netty 的数据容器&#xff0c;所有网络通信中字节流的传输都是通过 ByteBuf 完成的。 然而 JDK NIO 包中已经提供了类似的 ByteBuffer 类&#xff0c;为什么 Netty 还要去重复造轮子呢&#xff1f;本节课我会详细地讲解 ByteBuf。 JDK NIO…

Python(四十六)列表

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

如何查看发现板子存在哪些分区

marvsmart_rk3566_r:/ $ ls dev/block/by-name/ 参考&#xff1a;(189条消息) Android adb开机动画bootanimation.zip替换以及logo修改_安卓开机logo修改软件_爆炸哈斯卡的博客-CSDN博客

Verilog语法学习——LV1_四选一多路器

LV1_四选一多路器 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 制作一个四选一的多路选择器&#xff0c;要求输出定义上为线网类型 状态转换&#xff1a;…

C#再windowForm窗体中绘画扇形并给其填充颜色

C#再windowForm窗体中绘画扇形并给其填充颜色 Graphics graphics this.CreateGraphics();graphics.SmoothingMode SmoothingMode.AntiAlias;int width this.Width;int height this.Height;h this.Height;w this.Width;Rectangle rct new Rectangle(0 - h / 6, 0 - h / 6…

Verilog语法学习——LV5_位拆分与运算

LV5_位拆分与运算 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 现在输入了一个压缩的16位数据&#xff0c;其实际上包含了四个数据…

Vue3 word如何转成pdf代码实现

&#x1f642;博主&#xff1a;锅盖哒 &#x1f642;文章核心&#xff1a;word如何转换pdf 目录 1.前端部分 2.后端部分 在Vue 3中&#xff0c;前端无法直接将Word文档转换为PDF&#xff0c;因为Word文档的解析和PDF的生成通常需要在后端进行。但是&#xff0c;你可以通过Vu…

python和c加加有什么区别,c和c++和python先学哪个

本篇文章给大家谈谈c加加编程和python编程有什么区别&#xff0c;以及python和c加加有什么区别&#xff0c;希望对各位有所帮助&#xff0c;不要忘了收藏本站喔。 1、python和c学哪个好 学C好。 C通常比Python更快&#xff0c;因为C是一种编译型语言&#xff0c;而Python则是…

C# NDArray System.IO.FileLoadException报错原因分析

C# NDArray System.IO.FileLoadException 报错原因分析&#xff1a; 1.NuGet程序包版本有冲突 2.统一项目版本 1.打开解决方案NuGet程序包设置 2.查看是否有版本冲突 3.统一版本冲突

云原生全栈体系(一)

云平台核心 第一章 为什么用云平台 环境统一按需付费即开即用稳定性强 一、国内常见云平台 阿里云、百度云、腾讯云、华为云、青云… 二、国外常见云平台 亚马逊 AWS、微软 Azure … 三、公有云 购买云服务商提供的公共服务器 公有云是最常见的云计算部署类型。公有云资…

单元格JS复选框,带参按钮传值

一、复选框设置 1.第一个复选框 初始化后&#xff1a; setTimeout(function() {var isAllChecked true;//设置标记状态为选中var boxes _g().getWidgetsByName("boxes");//获取当前页的复选按钮控件数组if (typeof(boxes[0]) ! "undefined") {for (i …

Neural Network学习笔记4

完整的模型训练套路 train.py import torch import torchvision from torch.utils.data import DataLoader # 引入自定义的网络模型 from torch.utils.tensorboard import SummaryWriterfrom model import *# 准备数据集 train_data torchvision.datasets.CIFAR10(root"…

JAVASE---认识JAVASE

我们今天开始学习Java语言&#xff0c;首先Java是一种优秀的程序设计语言&#xff0c;它具有令人赏心悦目的语法和易于理解的语义。不仅如此&#xff0c;Java还是一个有一系列计算机软件和规范形成的技术体系&#xff0c;这个技术体系提供了完整的用于软件开发和跨平台部署的支…

零的奇幻漂移:解密数组中的神秘消失与重生

本篇博客会讲解力扣“283. 移动零”的解题思路&#xff0c;这是题目链接。 思路1 这道题目很有意思。虽然是简单题&#xff0c;其蕴含的玄机还是很多的。正常来讲&#xff0c;这种题目一般都会原地操作&#xff08;不开辟额外的数组&#xff0c;空间复杂度是O(1)&#xff09;&…

Debian12安装MySQL时报错缺少依赖libssl1.1(>= 1.1.1)

解决方案&#xff1a;进入镜像站&#xff0c;下载libssl包并安装&#xff0c;如&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/debian/pool/main/o/openssl/libssl1.1_1.1.1n-0%2Bdeb11u5_amd64.debIndex of /debian/pool/main/o/openssl/ | 清华大学开源软件镜像站 | Tsi…

用windeployqt.exe打包Qt代码

首先找到我们编译Qt代码的对应Qt版本的dll目录&#xff0c;该目录下有windeployqt.exe&#xff1a; D:\DevTools\Qt\5.9\msvc2017_64\bin 在这个目录下打开cmd程序。 然后把要打包的exe放到一个单独的目录下&#xff0c;比如&#xff1a; 然后在cmd中调用&#xff1a; winde…