【数据结构 04】单链表

news2024/9/23 11:20:47

一、链表简介

链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

链表在结构上的分类

1. 带头结点或无头结点
2. 单向或双向
3. 循环或非循环

虽然链表有多种结构类型,但是我么在实际开发中常用的只有两种结构:

  1. 无头单向非循环链表:结构简单,通常不单独使用,而是作为其他数据结构的子结构,如哈希桶、图的邻接表……
  2. 带头双向循环链表:结构最复杂,功能最全面,使用效率高

下例代码是无头单向非循环链表的实现,设计思路:

  1. 每个ListNode节点都包含一个数据和一个next指针,next指针指向下一个节点
  2. 当pList == NULL 的时候,代表这个链表为空,没有任何数据
  3. 链表最后一个节点的next指针一定是NULL
  4. 当函数涉及数据增删时,传入的参数为二级指针 ListNode** ppList

二、SingleList.h

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int DataType;

typedef struct ListNode
{
	DataType data;
	struct ListNode* next;
}ListNode;

bool Empty(ListNode* plist)
{
	return plist == NULL;
}

void Print(ListNode* plist)
{
	ListNode* cur = plist;
	while (cur != NULL)
	{
		printf("%2d -> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

// 动态申请一个节点
ListNode* BuyNode(DataType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->data = x;
	node->next = NULL;
	return node;
}

// 尾插
void PushBack(ListNode** pplist, DataType x)
{
	assert(pplist);
	ListNode* node = BuyNode(x);

	// 插入链表的第一个节点
	if (*pplist == NULL)
	{
		*pplist = node;
		return;
	}

	// cur指针通过循环遍历找到链表的尾结点
	ListNode* cur = *pplist;
	while (cur->next != NULL)
	{
		cur = cur->next;
	}
	cur->next = node;
}

// 头插
void PushFront(ListNode** pplist, DataType x)
{
	assert(pplist);
	ListNode* node = BuyNode(x);

	if (*pplist == NULL)
	{
		*pplist = node;
		return;
	}

	node->next = *pplist;
	*pplist = node;
}

// 尾删
void PopBack(ListNode** pplist)
{
	if (Empty(*pplist))
	{
		printf("链表为空,尾删失败\n");
		return;
	}

	ListNode* cur = *pplist;
	ListNode* prev = cur;
	while (cur->next != NULL)
	{
		prev = cur;
		cur = cur->next;
	}
	free(cur);
	cur = NULL;
	prev->next = NULL;
}

// 头删
void PopFront(ListNode** pplist)
{
	if (Empty(*pplist))
	{
		printf("链表为空,尾删失败\n");
		return;
	}

	ListNode* cur = *pplist;
	*pplist = cur->next;
	free(cur);
	cur = NULL;
}

// 查找,返回第一个元素x的节点
ListNode* Find(ListNode* plist, DataType x)
{
	ListNode* cur = plist;
	while (cur != NULL)
	{
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

// 在pos后面插入新节点,pos节点由Find函数获得
void InsertAfter(ListNode* pos, DataType x)
{
	if (pos == NULL)
	{
		printf("pos为空,数据插入失败\n");
		return;
	}
	ListNode* node = BuyNode(x);
	node->next = pos->next;
	pos->next = node;
}

// 删除pos节点
void Delete(ListNode** pplist, ListNode* pos)
{
	if (pos == NULL)
	{
		printf("pos为空,数据删除失败\n");
		return;
	}
	if (Empty(*pplist))
	{
		printf("单链表已为空,Delete失败\n");
		return;
	}

	ListNode* cur = *pplist;
	ListNode* prev = NULL;
	while (cur)
	{
		if (cur->data == pos->data && prev == NULL)
		{
			// 删除第一个节点
			*pplist = pos->next;
			free(pos);
			pos = NULL;
			return;
		}
		if (cur->data == pos->data)
		{
			prev->next = pos->next;
			free(pos);
			pos = NULL;
			return;
		}

		prev = cur;
		cur = cur->next;
	}
}

// 销毁链表
void Destroy(ListNode** pplist)
{
	while (*pplist)
	{
		ListNode* cur = *pplist;
		*pplist = cur->next;
		free(cur);
		cur = NULL;
	}
	printf("链表销毁成功\n");
}

三、test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SingleList.h"

int main()
{
	ListNode* plist = NULL;

	// 尾插数据
	PushBack(&plist, 1);
	PushBack(&plist, 3);
	PushBack(&plist, 5);
	PushBack(&plist, 7);
	Print(plist);

	// 头插数据
	PushFront(&plist, 2);
	PushFront(&plist, 4);
	PushFront(&plist, 6);
	PushFront(&plist, 8);
	Print(plist);

	// 尾删数据
	PopBack(&plist);
	PopBack(&plist);
	PopBack(&plist);
	Print(plist);

	// 头删数据
	PopFront(&plist);
	PopFront(&plist);
	PopFront(&plist);
	Print(plist);

	// 在查找的元素后面插入节点
	InsertAfter(Find(plist, 1), -1);
	InsertAfter(Find(plist, 2), -2);
	InsertAfter(Find(plist, -2), 22);
	Print(plist);

	// 删除查找到的节点
	Delete(&plist, Find(plist, -2));
	Delete(&plist, Find(plist, -1));
	Delete(&plist, Find(plist, 22));
	Delete(&plist, Find(plist, 100)); // pos为空,数据删除失败!
	Print(plist);

	// Delete删空链表
	Delete(&plist, Find(plist, 2));
	Delete(&plist, Find(plist, 1));
	Delete(&plist, Find(plist, 1)); // pos为空,数据删除失败!
	Print(plist);

	// 销毁链表,先插入数据测试
	PushBack(&plist, 1);
	PushBack(&plist, 2);
	PushBack(&plist, 3);
	Print(plist); // 1 -> 2 -> 3 -> NULL
	Destroy(&plist); // 链表销毁成功
	Print(plist);
}

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

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

相关文章

算法设计与分析实验:并查集与生成树

目录 一、情侣牵手 1.1 采用并查集的思想 1.2 采用动态规划的思想 二、账户合并 2.1 具体思路 2.2 思路呈现 2.3 代码实现 2.4 复杂度分析 三、连接所有点的最小费用 3.1 思路一&#xff1a;最小生成树 3.2 思路二&#xff1a;并查集 鸡汤 一、情侣牵手 力扣第765…

Django模型(五)

一、数据的条件查询 参考文档:QuerySet API 参考 | Django 文档 | Django 1.1、常用检索字段 字段检索,是在字段名后加 __ 双下划线,再加关键字,类似 SQL 语句中的 where 后面的部分, 如: 字段名__关键字 exact :判断是否等于value,一般不使用,而直接使用 =contai…

数据与资源可视化——长安链运维监控实践

前言 “链上的交易总量是多少”&#xff0c;“我的链上现在有多少区块了”&#xff0c;“节点是否存活无法第一时间感知到”&#xff0c;除sdk查询链上的相关信息外&#xff0c;今天我们介绍一种新的方式实现链上数据与相关资源的可视化的监控。 简介 监控链上数据以及链上节…

日志资源成本减少 35%:新东方可观测体系改造如何降本增效?

一分钟精华速览 在双减政策影响下&#xff0c;新东方面临业务缩减和资源紧张的局面&#xff0c;迫切需要技术调整和优化以应对成本压力并提高效率。面对人手减少、技术标准化不足和技术栈复杂等挑战&#xff0c;公司制定了通过建立标准化的可观测性体系来提升运维和研发效率的…

初始化爱情的构造之旅

初始化爱情的构造之旅 The Constructive Journey of Initializing Love 在一个名为“编程之城”的奇幻世界里&#xff0c;住着两位年轻的程序员——林浩然和杨凌芸。林浩然是Java王国中的首席对象设计师&#xff0c;擅长用代码构建复杂而精巧的对象&#xff1b;而杨凌芸则是数据…

专业138总分420+中国科学技术大学843信号与系统考研经验中科大电子信息通信

**今年中科大专业课843信号与系统138分&#xff0c;总分420顺利上岸&#xff0c;梦圆中科大&#xff0c;也是报了高考失利的遗憾&#xff0c;总结一下自己的复习经历&#xff0c;希望可以给大家提供参考。**首先&#xff0c;中科大843包括信号与系统&#xff0c;和数字信号处理…

Cache Lab:Part B- 32 ×32【分块算法】

任务描述 在B部分中&#xff0c;您将在trans.c中编写一个转置函数&#xff0c;从而导致尽可能少的miss。缓存的参数位 (s 5, E 1, b 5)。三种测试用例的矩阵大小分别为&#xff1a; • 32 32 (M 32, N 32) • 64 64 (M 64, N 64) • 61 67 (…

继电器模块详解

继电器&#xff0c;一种常见的电控制装置&#xff0c;其应用几乎无处不在。在家庭生活&#xff0c;继电器被广泛应用于照明系统、电视机、空调等电器设备的控制&#xff1b;在工业领域&#xff0c;它们用于控制电机、泵站、生产线等高功率设备的运行&#xff1b;继电器还在通信…

mini-spring|设计与实现资源加载器并从Spring.xml解析和注册Bean对象

**需求&#xff1a;**我们需要自动为bean加载资源&#xff0c;代替注册、注入属性、注入bean等功能 1.资源加载器属于相对独立的部分&#xff0c;它位于 Spring 框架核心包下的IO实现内容&#xff0c;主要用于处理Class、本地和云环境中的文件信息。 2.当资源可以加载后&#…

假期刷题打卡--Day18

1、MT1168阶乘数 输入正整数N&#xff0c;找出它是否是一个等于其他数的阶乘值的数&#xff0c;输出YES或者NO。 格式 输入格式&#xff1a; 输入正整数N 输出格式&#xff1a; 输出YES或者NO 样例 1 输入&#xff1a; 5输出&#xff1a; NO 相关知识点 阶乘 可以理…

研发人员如何做好日常工作的稳定性保障

一、前言 二、稳定性介绍 三、实际操作流程 1、需求分析阶段 2、设计阶段 2、1备选架构 2、2方案设计 2、3 架构设计 2、4设计的checklist 2、5的checklist 3、开发联调 4、自测环节 5、上线前环节 6、上线后的验收和复盘 四、稳定性、效率、成本之间的考量 五、…

12306 真的很拉跨吗?春运是对它最大的误解!

春节降至&#xff0c;大家都抢到火车票了吗&#xff1f;马上就要迎来春节&#xff0c;是不是都在吐槽 12306 的种种不好&#xff0c;它真的有这么拉跨吗&#xff1f; 其实不然&#xff0c;每到各种节假日&#xff0c;都是对 12306 最大的误解&#xff01; 特别是春运&#xf…

Vite+Vue3使用Vue-i18n笔记

一、下载依赖 vue-i18n yarn add vue-i18n创建存放语言文件的目录 以及配置文件的配置 我是在src/lang 新建index.ts、cn.ts、en.ts以及test文件夹其中再分别新建cn.ts以及en.ts /lang/index.ts 用于导出vue-i18n需要的配置对象 import en from "./en.ts"; import…

PYTHON蓝桥杯——每日一练(简单题)

题目 利用字母可以组成一些美丽的图形&#xff0c;下面给出了一个例子&#xff1a; ABCDEFG BABCDEF CBABCDE DCBABCD EDCBABC 这是一个5行7列的图形&#xff0c;请找出这个图形的规律&#xff0c;并输出一个n行m列的图形。 输入格式 输入一行&#xff0c;包含两个整数…

[ESXi 8]安装centos7

文章目录 创建虚拟机创建虚拟机选择centos7选择存储选择镜像文件上传ios镜像文件 安装即将完成 启动虚拟机自动获取ip设置root密码安装成功 创建虚拟机 创建虚拟机 选择centos7 选择存储 选择镜像文件 上传ios镜像文件 如图显示上传进度&#xff0c;上传完毕之后&#xff0c;将…

【讲座分享】| 复旦大学张奇教授——《自然语言发表论文如何打怪升级?NLP顶会论文发表》

文章目录 1 基础关1.1 基础书籍1.2 提高书籍1.3 课程链接1.4 编程实战 2 阅读关2.1 分层过滤2.2 集团作战&#xff0c;信息获取2.3 论文如何泛读 3 动机 方向关3.1 快速发论文3.2 好的研究 4 写作关4.1 论文写作流程4.2 从读者角度出发4.3 每一部分怎么写4.3.1 Abstract摘要4.3…

一体化设计:兼容多种OS系统Linux网关楼宇DDC

在工业物联网&#xff08;IIoT&#xff09;和智能建筑领域&#xff0c;钡铼网关具备高度灵活性与强大计算能力的边缘网关产品正逐渐成为推动行业智能化转型的关键要素。本文将详细介绍的基于Linux系统的4G工业智能网关&#xff0c;不仅拥有NXP i.MX8M Mini四核64位处理器的强大…

直播观看人次破30W | 极新「2024未来直播电商科技峰会」圆满落幕

“共话直播电商&#xff06;消费科技行业破局之道” 文&#xff5c;德江&凯丰 编辑 | 云舒 出品&#xff5c;极新 1月27日&#xff0c;由极新携手北京电子商务协会联合举办的「2024未来直播电商科技峰会」圆满落幕&#xff01;在峰会上&#xff0c;共进行了10 场演讲 &a…

【C++】笔试训练(八)

目录 一、选择题二、编程题1、两种排序方法2、求最小公倍数 一、选择题 1、关于重载函数&#xff0c;哪个说明是正确的&#xff08;&#xff09; A 函数名相同&#xff0c;参数类型或个数不同 B 函数名相同&#xff0c;返回值类型不同 C 函数名相同&#xff0c;函数内部实现不…

018 用户交互Scanner

什么是Scanner对象 next()方法 // 声明输入对象 Scanner scanner new Scanner(System.in);System.out.println("next()方法接收&#xff1a;"); if (scanner.hasNext()) {// 输入 Hello worldString str1 scanner.next();// 输出 HelloSystem.out.println(str1); }…