【数据结构初阶】第四篇——双向链表

news2024/9/22 23:21:09

链表介绍

初始化链表

销毁链表

打印双向链表

查找数据

增加结点

头插

尾插

在指定位置插入

删除结点

头删

尾删

删除指定位置

链表判空

获取链表中元素个数

顺序表和链表对比

存取方式

逻辑结构与物理结构

时间性能

空间性能


链表介绍

本章讲的是带头双向链表。这里的头不存放任何数据,就是一个哨兵卫的头结点。

用代码来表示每一个结点:

typedef int LTDataType;//存储的数据类型

typedef struct ListNode
{
	LTDataType data;//数据域
	struct ListNode* prev;//前驱指针
	struct ListNode* next;//后继指针
}ListNode;

初始化链表

初始化链表中,我们要开好一个头结点,作为哨兵卫的头结点,然后返回这个结点的指针,接口外面只要用一个结点指针接受这个返回值就好了,具体实现如下:

//创建一个新结点
ListNode* BuyListNode(LTDataType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	node->data = x;//新结点赋值
	node->prev = NULL;
	node->next = NULL;

	return node;//返回新结点
}

//初始化链表
ListNode* ListInit()
{
	ListNode* phead = BuyListNode(-1);//申请一个头结点,头结点不存储有效数据
	//起始时只有头结点,让它的前驱和后继都指向自己
	phead->prev = phead;
	phead->next = phead;
	return phead;//返回头结点
}

销毁链表

申请的节点使用完之后都要自己手动释放,以防止内存泄漏这些不好的问题出现。我们实现这个接口,用一级指针接受实参,其实也是遍历一遍链表,看一下代码实现:

//销毁链表
void ListDestroy(ListNode* phead)
{
	assert(phead);

	ListNode* cur = phead->next;//从头结点后一个结点开始释放空间
	ListNode* next = cur->next;//记录cur的后一个结点位置
	while (cur != phead)
	{
		free(cur);
		cur = next;
		next = next->next;
	}
	free(phead);//释放头结点
}

打印双向链表

双链表的打印就是遍历一遍双链表,用一个cur节点指针来走,走到head的位置就停下来。
看代码实现:

//打印双向链表
void ListPrint(ListNode* phead)
{
	assert(phead);

	ListNode* cur = phead->next;//从头结点的后一个结点开始打印
	while (cur != phead)//当cur指针指向头结点时,说明链表以打印完毕
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

查找数据

查找无非就是遍历双链表,这是还是直接上代码实现:

//查找元素
ListNode* ListFind(ListNode* phead, LTDataType x)
{
	assert(phead);

	ListNode* cur = phead->next;//从头结点的后一个结点开始查找
	while (cur != phead)//当cur指向头结点时,说明链表已遍历完毕
	{
		if (cur->data == x)
		{
			return cur;//返回目标结点的地址
		}
		cur = cur->next;
	}
	return NULL;//没有找到目标结点
}

增加结点

头插

 头插,即申请一个新结点,将新结点插入在头结点和头结点的后一个结点之间即可。

//头插
void ListPushFront(ListNode* phead, LTDataType x)
{
	assert(phead);

	ListNode* newnode = BuyListNode(x);//申请一个结点,数据域赋值为x
	ListNode* front = phead->next;//记录头结点的后一个结点位置
	//建立新结点与头结点之间的双向关系
	phead->next = newnode;
	newnode->prev = phead;
	//建立新结点与front结点之间的双向关系
	newnode->next = front;
	front->prev = newnode;
}

尾插

 尾插,申请一个新结点,将新结点插入到头结点和头结点的前一个结点之间即可。因为链表是循环的,头结点的前驱指针直接指向最后一个结点,所以我们不必遍历链表找尾。

//尾插
void ListPushBack(ListNode* phead, LTDataType x)
{
	assert(phead);

	ListNode* newnode = BuyListNode(x);//申请一个结点,数据域赋值为x
	ListNode* tail = phead->prev;//记录头结点的前一个结点的位置
	//建立新结点与头结点之间的双向关系
	newnode->next = phead;
	phead->prev = newnode;
	//建立新结点与tail结点之间的双向关系
	tail->next = newnode;
	newnode->prev = tail;
}

在指定位置插入

 在直到位置插入结点,准确来说,是在指定位置之前插入一个结点。我们只需申请一个新结点插入到指定位置结点和其前一个结点之间即可。

//在指定位置插入结点
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);

	ListNode* before = pos->prev;//记录pos指向结点的前一个结点
	ListNode* newnode = BuyListNode(x);//申请一个结点,数据域赋值为x
	//建立新结点与before结点之间的双向关系
	before->next = newnode;
	newnode->prev = before;
	//建立新结点与pos指向结点之间的双向关系
	newnode->next = pos;
	pos->prev = newnode;
}

删除结点

头删

头删,即释放头结点的后一个结点,并建立头结点与被删除结点的后一个结点之间的双向关系即可。

//头删
void ListPopFront(ListNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	ListNode* front = phead->next;//记录头结点的后一个结点
	ListNode* newfront = front->next;//记录front结点的后一个结点
	//建立头结点与newfront结点之间的双向关系
	phead->next = newfront;
	newfront->prev = phead;
	free(front);//释放front结点
}

尾删

尾删,即释放最后一个结点,并建立头结点和被删除结点的前一个结点之间的双向关系即可。

//尾删
void ListPopBack(ListNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	ListNode* tail = phead->prev;//记录头结点的前一个结点
	ListNode* newtail = tail->prev;//记录tail结点的前一个结点
	//建立头结点与newtail结点之间的双向关系
	newtail->next = phead;
	phead->prev = newtail;
	free(tail);//释放tail结点
}

删除指定位置

 删除指定位置结点,释放掉目标结点后,建立该结点前一个结点和后一个结点之间的双向关系即可。

//删除指定位置结点
void ListErase(ListNode* pos)
{
	assert(pos);

	ListNode* before = pos->prev;//记录pos指向结点的前一个结点
	ListNode* after = pos->next;//记录pos指向结点的后一个结点
	//建立before结点与after结点之间的双向关系
	before->next = after;
	after->prev = before;
	free(pos);//释放pos指向的结点
}

链表判空

//链表判空
bool ListEmpty(ListNode* phead)
{
	assert(phead);

	return phead->next == phead;//当链表中只有头结点时为空
}

获取链表中元素个数

//获取链表中的元素个数
int ListSize(ListNode* phead)
{
	assert(phead);

	int count = 0;//记录元素个数
	ListNode* cur = phead->next;//从头结点的后一个结点开始遍历
	while (cur != phead)//当cur指向头结点时,遍历完毕,头结点不计入总元素个数
	{
		count++;
		cur = cur->next;
	}
	return count;//返回元素个数
}

顺序表和链表对比

存取方式

顺序表:可以顺序存取,也可以随机存取;

单链表:只能从表头顺序进行存取元素;

逻辑结构与物理结构

顺序表:逻辑上相邻的元素,对应的物理存储位置也相邻;

单链表:逻辑上相邻的元素,物理存储位置不一定相邻,其逻辑关系是通过指针链接来表示的;

时间性能

顺序表支持随机访问,时间复杂度为O(1);

顺序表插入和删除需要依次移动元素,时间复杂度为O(N);

单链表要访问指定元素,需要从头遍历,时间复杂度为O(N);

单链表的插入和删除不需要移动元素,时间复杂度为O(1);

空间性能

顺序表:需要预先分配存储空间,分配过多就会造成存储空间的浪费,而分配过少则会发生上溢。动态存储分配虽然存储空间可以扩充,但需要移动大量元素,导致操作效率降低,而且若内存中没有更大的连续存储空间,则会导致分配失败。
单链表:按需分配,且元素个数不受限制 

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

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

相关文章

回溯算法秒杀所有排列-组合-子集问题

🌈🌈😄😄 欢迎来到茶色岛独家岛屿,本期将为大家揭晓LeetCode 78. 子集 90. 子集 II 77. 组合 39. 组合总和 40. 组合总和 II 47. 全排列 II,做好准备了么,那么开始吧。 🌲&#x1f…

上海亚商投顾:A股两市震荡走弱 北证50指数大涨5.8%

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。市场情绪沪指今日震荡调整,创业板指午后一度跌近1.5%,黄白二线分化明显,题材概念表现活跃…

Redis快速入门

Redis快速入门&#xff0c;分两个客户端&#xff1a;Jedis和SpringDataRedis 使用Jdedis 1、引入依赖 <!--jedis--> <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</version>…

python算法面试题

这是我年前做技术面试官&#xff0c;搜集的面试题&#xff0c;从python基础-机器学习-NLP-CV-深度学习框架-Linux-yolo都有一些题目。针对不同方向的应试者问相应方向的问题。 基本上都是面试八股文&#xff0c;收集记录一下&#xff0c;以后自己也会用的到。 面试题 python基…

深入理解mysql的内核查询成本计算

MySql系列整体栏目 内容链接地址【一】深入理解mysql索引本质https://blog.csdn.net/zhenghuishengq/article/details/121027025【二】深入理解mysql索引优化以及explain关键字https://blog.csdn.net/zhenghuishengq/article/details/124552080【三】深入理解mysql的索引分类&a…

过年回家,你是否也努力的给别人解释软件开发是干啥滴?

这个年就这样&#xff0c;在喜气洋洋的气氛中&#xff0c;在我们依依不舍的留恋中&#xff0c;从我们身边溜走了。这次回家又碰见了亲戚们不厌其烦的问我&#xff0c;你做什么工作呐&#xff1f;于是就有了我以下生动的解释 目录 打字的 帮助传话&#xff0c;帮助卖东西 皮…

易点易动打通固定资产采购,为企业实现降本增效

企业为什么要实现采购和资产管理的连接&#xff1f; 随着科技的发展&#xff0c;企业的办公工具越来越多&#xff0c;各类办公软件数不胜数。随之而来的是数据的不连通&#xff0c;员工需要穿梭在各个办公软件&#xff0c;重复导入导出数据&#xff0c;无形中没有提升办公效率…

三维电子沙盘数字沙盘开发教程第3课

三维电子沙盘数字沙盘开发教程第3课下面介绍矢量图层的控制显示&#xff1a;上代码foreach(string key in gis3d.SetFile.Biao.Keys)// gis3d.SetFile.Biao 该对象里存储了所有矢量层的信息{gis3d.SetFile.Biao[key].Show true; //是否显示标签gis3d.SetFile.Biao[key].ShowTe…

1.Weisfeiler-Lehman Algorithm

文章目录1.图同构介绍2.Weisfeiler-Lehman Algorithm3.后话参考资料欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; Weisfeiler-Lehman Algorithm是美国的数学家Boris Weisfeiler在1968年发表的论文the reduction of a graph to a canonic…

Flask WebSocket学习笔记

WebSocket简介&#xff1a;WebSocket是一种全新的协议&#xff0c;随着HTML5的不断完善&#xff0c;越来越多的现代浏览器开始全面支持WebSocket技术了&#xff0c;它将TCP的Socket&#xff08;套接字&#xff09;应用在了webpage上&#xff0c;从而使通信双方建立起一个保持在…

Java面向对象基础

文章目录面向对象一、类和对象1. 类的介绍2. 类和对象的关系3. 类的组成4. 创建对象和使用对象的格式二、对象内存图1. 单个对象内存图2. 两个对象内存图3. 两个引用指向相同内存图三、成员变量和局部变量四、this 关键字1. this 可以解决的问题2. this 介绍3. this 内存图五、…

学术的快乐来源

文章目录我的核心快乐来源&#xff1a;其他非核心快乐源泉知乎搜到的别人的快乐来源作此文&#xff0c;以便懈怠时候看看&#xff0c;能够聊表安慰。 ——题记 抱着给自己枯燥无聊学术生涯找点乐子的想法&#xff0c; 我决定仔细思考一下自己做学术的时候有哪些快乐的地方&…

C++关于开源包7zip压缩工具的编译及使用

1、7zip的配置 7-Zip是一款免费开源的压缩与解压软件&#xff0c;基本能够满足绝大多数常见的压缩和解压文件需求&#xff0c;此外还支持了分卷压缩和解压&#xff0c;非常好用。但是调用7-zip库需要用到一个叫bit7z的库&#xff0c;bit7z是一个C静态库&#xff0c;其封装了简单…

MySQL 中主从之间是怎样保证数据一致的呢?

在我们日常的工作中&#xff0c;处理 MySQL 数据库相关问题时&#xff0c;我相信绝大多数 DBA 处理最棘手的问题就是数据库主从数据不一致的问题。 处理过关于 MySQL 数据库主从数据不一致的朋友一定印象非常深刻&#xff0c;因为稍有不慎就会将造成原有数据的丢失&#xff0c…

精益安灯电子看板实现了实时监测

众所周知&#xff0c;智能工厂的规划建设是一个十分复杂的系统工程。所以安灯电子看板是精益生产中一一个重要组成要素&#xff0c;可以提升工厂生产车间的过程管理&#xff0c;生产数据做的信息化、目视化&#xff1b;信息快捷化、生产工序透明化等&#xff0c;是提高生产率的…

自动控制原理笔记-根轨迹的概念-根轨迹方程

目录 根轨迹的基本概念&#xff1a; 根轨迹的概念&#xff1a;当开环系统某一参数从 0 到∞变化时&#xff0c;闭环极点在S 平面上变化所描绘出的轨迹。 闭环零极点与开环零极点之间的关系&#xff1a; 根轨迹方程&#xff1a; 开环增益于根轨迹间的关系&#xff1a; 闭环系…

excel 格式化日期为字符串

最近经常遇到excel打开文件的时候&#xff0c;excel自动将yyyy-MM-dd HH:mm:ss &#xff08;如&#xff1a;2022-01-21 12:12:12 &#xff09;之类的时间的自动转为这样的格式列&#xff0c;2022/1/21 12:12:12 &#xff0c;导致有想从excel/csv格式 中复制原始日期格式比较麻烦…

【软件测试】一个真正的测试面试过程,我比面试官还狡猾......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 先卖个关子&#xf…

关羽这灵敏度你们爱了吗?#走位 #游戏外设

关羽这灵敏度你们爱了吗&#xff1f;#走位 #游戏外设 关羽这灵敏度你们爱了吗&#xff1f;#走位 #游戏外设

var、let、const之间的区别

说一下var、let、const之间的区别一、var二、let三、const四、var、let 、const的区别&#xff1f;一、var 用var声明的变量既是全局变量&#xff0c;也是顶层变量 注意&#xff1a;顶层对象&#xff0c;在浏览器环境指的是window对象&#xff0c;在Node指的是global对象。 var…