链表详讲(附代码)

1.什么是链表

链表是一种非常常见的数据结构,在程序设计中经常被使用。它由一系列节点组成,每个节点都有用来存放数据的数据区以及存放下一个节点地址指针的地址区。跟顺序表不同的是,链表的节点之间的空间并非是连续的,依靠地址区的值找到下一个节点的位置,这样相互链接成的链状结构我们称之为链表。

2.链表的优缺点

链表和顺序表其实是互补的好兄弟,换句话来说,顺序表的优点是链表的缺点,而链表的优点又恰恰是顺序表的缺点。

2.1链表的优点

链表的优点在于它的灵活性,相比于数组,链表可以更方便地进行插入和删除操作,这是因为链表的节点可以在内存中任意位置创建和删除,而数组需要通过整体移动来实现插入和删除操作,效率较低。

相比于顺序表,链表一般不会造成空间的浪费,而顺序表则会因为扩容的问题往往开辟出较大的空间,会存在一定的浪费。

2.2链表的缺点

链表的缺点在于查找,它在随机访问和查找元素时效率不如数组,因为每个节点只能通过指针一步一步向下查找。

先比于顺序表,顺序表查找的时间可以达到O(1),而链表通常是O(n).

2.3总结

根据链表的优点,我们可以在频繁插入、删除操作但不需要随机访问和查找元素时使用这种数据结构。

例如,在计算机科学中,许多常见问题,如哈希表、图、堆栈、队列等,都可以使用链表作为基础数据结构来实现。

3.代码模拟链表(c语言)

3.1功能函数的声明:

SList.h头文件:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.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);

功能呢,无非就是常见的各种增、删、查、改。

但是值得注意的是,由于我这里链表的节点是结构体,用head结构体指针来指向该链表的头节点,所以在传参的时候要注意一级指针和二级指针的区别。

比如说假如我们要修改head指向的节点,这个时候我们修改的值是一个指针,所以传参进来要用二级指针。

3.2功能函数的实现

SList.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x) {
	SListNode* res = (SListNode*)malloc(sizeof(SListNode));
	res->data = x;
	res->next = NULL;
	return res;
}

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

	printf("NULL \n");
}

// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x) {
	assert(pplist);
	SListNode* temp = BuySListNode(x);
	if (*pplist == NULL) {//当前链表没有节点
		*pplist = temp;
	}
	else {
		SListNode* tail = *pplist;
		while (tail->next != NULL) {
			tail = tail->next;
		}
		temp->next = NULL;
		tail->next = temp;

		//*pplist = tail;
	}
	return;

}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x) {
	assert(pplist);
	SListNode* temp = BuySListNode(x);
	if (*pplist == NULL) {//当前链表没有节点
		*pplist = temp;
	}
	else {
		temp->next = *pplist;
		*pplist = temp;
	}
	return;
}
// 单链表的尾删
void SListPopBack(SListNode** pplist) {
	assert(pplist && *pplist);
	SListNode* tail = *pplist;
	if ((*pplist)->next == NULL) {//只有一个节点
		free(*pplist);
		*pplist = NULL;
	}
	else {
		SListNode* pre = NULL;
		while (tail->next != NULL) {
			pre = tail;
			tail = tail->next;
		}
		free(tail);
		pre->next = NULL;
	}
}
// 单链表头删
void SListPopFront(SListNode** pplist) {
	assert(pplist&&*pplist);
	if ((*pplist)->next==NULL) {//只有一个节点
		free(*pplist);
		*pplist = NULL;
	}
	else {
		SListNode* temp = (*pplist)->next;
		free(*pplist);
		*pplist = temp;
	}
}
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x) {
	assert(plist);
	while (plist!=NULL) {
		if (plist->data == x) {
			return plist;
		}
		plist = plist->next;
	}
	return NULL;
}
// 单链表在pos位置之后插入x
// 分析思考为什么不在pos位置之前插入?
void SListInsertAfter(SListNode* pos, SLTDateType x) {
	assert(pos);
	SListNode* temp = BuySListNode(x);
	if (pos->next == NULL) {
		pos->next = temp;
		temp->next = NULL;
	}
	else {
		temp->next = pos->next;
		pos->next = temp;
	}
}
// 单链表删除pos位置之后的值
// 分析思考为什么不删除pos位置?
void SListEraseAfter(SListNode* pos) {
	assert(pos&&pos->next);
	SListNode* temp = pos->next->next;
	free(pos->next);
	pos->next = temp;
}
// 在pos的前面插入
void SLTInsert(SListNode** pphead, SListNode* pos, SLTDateType x) {
	assert(*pphead && pos);
	SListNode* temp = BuySListNode(x);
	if ((*pphead)->next == NULL||(*pphead)==pos) {
		temp->next = (*pphead);
		*pphead = temp;
	}
	else {
		SListNode* pre = NULL;
		SListNode* cur = *pphead;
		while (cur->next!= NULL) {
			if (cur== pos)break;
			pre = cur;
			cur = cur->next;
		}	
		temp->next = pre->next;
		pre->next = temp;
	}

}
// 删除pos位置
void SLTErase(SListNode** pphead, SListNode* pos) {
	assert(*pphead && pos);
	if ((*pphead) == NULL) {
		free(*pphead);
		*pphead = NULL;
	}
	else {
		SListNode* pre = NULL;
		SListNode* cur = *pphead;
		while (cur->next != NULL) {
			if (cur == pos)break;
			pre = cur;
			cur = cur->next;
		}
		SListNode* temp = cur->next;
		free(cur);
		if (pre == NULL) {
			*pphead = temp;
		}
		else {
			pre ->next= temp;
		}
	}
}
//销毁链表
void SLTDestroy(SListNode** pphead) {
	assert(*pphead);
	SListNode* ph = *pphead;
	while (ph!= NULL) {
		SListNode* temp = ph->next;
		free(ph);
		ph = temp;
	}
	*pphead = NULL;
}

由于是用malloc动态开辟空间,在删除操作的时候我们要使用free及时释放,避免照成空间泄露的问题。

代码经过测试应该是没有太大的问题,基本功能也都能实现。

这里我想强调一点,为什么我们要模拟链表的功能呢?

模拟各种数据结构的功能可以让我们理解得更加透彻,能较为清晰的明白各种数据结构内部代码大概是怎么实现的。我认为,这对数据结构的熟练运用是有非常大的帮助的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【python VS vba】(5) 在python中使用xlwt操作Excel(待完善ing)

目录 1 什么是xlwt 2 导入xlwt 3 相关语法 3.1 创建新的workbook 3.2 创建新的sheet 3.3 保存workbook 4 python里表格的形式 4.1 矩阵 4.2 EXCEL的数据形式 完全等于矩阵的数字结构 4.3 python里矩阵 5 具体代码 5.1 代码 5.2 结果 5.3 要注意的问题 5.3.1 不能…

STL-set和map

目录 一、pair和make_pair 1. pair 2. make_pair 二、set &#xff08;一&#xff09;set的模板参数列表 &#xff08;二&#xff09;set的构造 &#xff08;三&#xff09;set的插入 1. 测试1 2. 测试2 &#xff08;四&#xff09;low_bound和upper_bound&#xff…

(四)docker:为mysql和java jar运行环境创建同一网络,容器互联

看了很多资料&#xff0c;说做互联的一个原因是容器内ip不固定&#xff0c;关掉重启后如果有别的容器启动&#xff0c;之前的ip会被占用&#xff0c;所以做互联创建一个网络&#xff0c;让几个容器处于同一个网络&#xff0c;就可以互联还不受关闭再启动ip会改变的影响&#xf…

ArcGIS for Android 禁止地图旋转

ArcGIS for Android 禁止地图旋转 话不多说&#xff0c;直接上代码&#xff01;&#xff01;&#xff01; public class LoadMap extends AppCompatActivity {// 地图private MapView mapView;private ArcGISMap map;Overrideprotected void onCreate(Bundle savedInstanceSta…

Ubuntu 系统内核 kernel panic

Ubuntu 系统内核 kernel panic 不能进入系统&#xff1a;报错end kernel panic -not syncing: attemped to kill init! exit code 0x00000100 系统启动的时候&#xff0c;按下‘e’键进入grub编辑界面&#xff0c;编辑grub菜单&#xff0c;选择“kernel /vmlinuz-XXXXro root…

听听ChatGPT对IT行业的发展和就业前景的看法

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏:PYTHON学习系列专栏&#x1f4ab;"没有罗马,那就自己创造罗马~" 目录 (1)判断素数 写法1: 写法2: (2)计算1-100的偶数之和 写法1: 写法2: (3)计算1-100的奇数之和 (4)多层循环 IT行业哪个方向比较…

k8s 多网卡方案multus

kubernetes 多网卡方案之 Multus_CNI 部署以及基本使用 一、multus cni 出现的背景 在k8s的环境中启动一个容器&#xff0c;默认情况下只存在两个虚拟网络接口&#xff08;loopback 和 eth0&#xff09;&#xff0c; loopback 的流量始终都会在本容器内或本机循环&#xff0c…

【中国知名企业高管团队】系列57:康佳KONKA

今天开始&#xff0c;华研荟为大家介绍中国电视行业的知名企业&#xff0c;接下来三天介绍位于深圳的电视三巨头&#xff0c;这三巨头以电视机研发、生产和销售起步&#xff0c;2000左右生产过非智能手机&#xff0c;但是在互联网时代被小米们抢走了电视和手机的很大一部分市场…

【音视频 | opus】opus编解码库(opus-1.4)详细介绍以及使用——附带解码示例代码

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

《动态顺序表》的实现

目录 前言&#xff1a; 认识线性表&#xff1a; 关于顺序表 实现动态顺序表&#xff1a; 顺序表的动态存储定义&#xff1a; 初始化顺序表&#xff1a; 顺序表的销毁&#xff1a; 尾插&#xff1a; 判断是否需要扩容&#xff1a; 总代码&#xff1a; 头插&#xff1a…

C++——类和对象(中)完结

赋值运算符重载 运算符重载 C 为了增强代码的可读性引入了运算符重载 &#xff0c; 运算符重载是具有特殊函数名的函数 &#xff0c;也具有其 返回值类型&#xff0c;函数名字以及参数列表&#xff0c;其返回值类型与参数列表与普通的函数类似。 函数名字为&#xff1a;关键…

父子进程之间的等待(wait和waitpid的介绍+原理),status的介绍+恢复退出码(位运算+宏),非阻塞等待(宏),signal查看

目录 父子进程之间的等待 介绍 为什么要有等待 内存泄漏 如何等待 介绍 pid_t wait (int* status) 介绍 status指针 示例 ​编辑 pid_t waitpid (pid_t pid,int* status,int options) pid options WNOHANG -- 非阻塞等待 示例 status 查看status status问题 …

Mybatis与Mybatis-Plus(注解与Xml)(单表与多表)

准备工作 这里我们准备了两个与数据库表对应的实体类&#xff0c;stu为学生表&#xff0c;cls为班级表 类属性上的注解如 TableId等 为Mybatis-Plus的注解&#xff0c;使用mybatis会无视掉这些注解 在Stu 类的最后一个属性我们定义了Cls实体类的对象&#xff0c;对于单表查询&…

EASYX输出文字

在EASYX中绘制出字符串和字符 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <easyx.h> #include <iostream> #include <math.h> #include <stdlib.h> #include <conio.h> #include <time.h> #define PI 3.14、 //…

Windows 下编译 TensorFlow 2.9.1 CC库

参考 Intel 的 tensorflow 编译指导&#xff0c;不过项目还是可以用 TF原本的&#xff0c;不是一定要选择Intel 的TF版本。 安装 MSVC 2019 安装 Intel OneDNN OneMKL 似乎也可以不安装 ( & ) https://www.intel.cn/content/www/cn/zh/developer/articles/tool/one…

算法随想录算法训练营第四十七天| 647. 回文子串 516.最长回文子序列

647. 回文子串 题目&#xff1a;给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字…

开发知识点-PHP从小白到拍簧片

从小白到拍簧片 位异或运算&#xff08;^ &#xff09;引用符号(&)strlen() 函数base64_encode预定义 $_POST 变量session_start($array);操作符php 命令set_time_limit(7200)isset()PHP 命名空间(namespace)new 实例化类extends 继承 一个类使用另一个类方法error_reporti…

【C语法学习】16 - fclose()函数

文章目录 1 函数原型2 参数3 返回值4 示例 1 函数原型 fclose()&#xff1a;关闭已打开的文件&#xff0c;并刷新缓冲区&#xff0c;函数原型如下&#xff1a; int fclose(FILE *stream);2 参数 fclose()函数只有一个参数stream&#xff1a; 参数stream是一个指向FILE类型结…

Daily neaty和希亦内衣洗衣机哪款好,高性价比内衣洗衣机测评

现在市面最火的小家电莫过于是内衣洗衣机&#xff0c;那么它是否真的好用还是只是智商税呢&#xff1f;但关于内衣洗衣机&#xff0c;很多小伙伴都会选入手来释放自己的双手的&#xff0c;现在内衣洗衣机品牌众多&#xff0c;而且Daily neaty和希亦CEYEE-ACE这两个大品牌会被许…

【漏洞复现】Apache_HTTPD_换行解析漏洞(CVE-2017-15715)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 文章目录 1.1、漏洞描述1.2、漏洞等级1.3、影响版本1.4、漏洞复现1、基础环境2、漏洞扫描3、漏洞验证 1.5、深度利用GetShell 1.6、修复建议 说明内容漏洞编号CVE-2017-15715漏洞名称Ap…