数据结构笔记第3篇:双向链表

1、双向链表的结构

注意:这里的 "带头" 跟前面我们说的 "头结点" 是两个概念,实际前面的在单链表阶段称呼不严谨,但是为了同学们更好的理解就直接称为单链表的头结点。

带头链表里的头结点,实际为 "哨兵位" ,哨兵位节点不存储任何有效元素,只是站在这里 "放哨的"。

"哨兵位" 存在的意义:

遍历循环链表避免死循环。

注意:双向链表中,哨兵位的下一个节点是链表的第一个节点(头结点)。哨兵位的前一个节点是链表的最后一个节点(尾结点)。所以双向链表的头插是在哨兵位的后面插入数据。尾插则是在哨兵位之前插入数据。

哨兵位是作为头结点和尾结点的中点,是头结点的起点也是尾节点的终点。这样解释更容易理解。

2、 双向链表的实现

List.h 链表函数链表节点类型的声明:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLDataType;
typedef struct ListNode
{
	SLDataType data;//存储数据
	struct ListNode* prve;//存储前一个节点的地址
	struct ListNode* next;//存储下一个节点的地址
}SList;
SList* SLInit();

void SLPushBack(SList* phead, SLDataType x);//尾插
void SLPrint(SList* phead);//显示链表数据
void SLPustFront(SList* phead, SLDataType x);//头插
void SLPopBack(SList* phead);//尾删
void SLPopFront(SList* phead);//头删
void SLInsert(SList* pos, SLDataType x);//指定位置插入
SList* SLfind(SList* phead, SLDataType x);//查找节点
void SLEarse(SList* pos);//指定位置删除
void SLDestory(SList** pphead);//链表销毁

List.c 链表函数的实现:

#include "List.h"
//链表初始化
SList* SLInit()
{
	SList* phead = (SList*)malloc(sizeof(SList));
	if (phead == NULL)
	{
		perror("malloc error");
		return NULL;
	}
	phead->data = -1;
	//因为是循环链表,所以初始化要遵循循环格式
	phead->next = phead;
	phead->prve = phead;
	return phead;
}
//创建链表节点
SList* ListBuyNode(SLDataType x)
{
	SList* retNode = (SList*)malloc(sizeof(SList));
	if (retNode == NULL)
	{
		perror("malloc error");
		return NULL;
	}
	retNode->data = x;
	retNode->prve = retNode;
	retNode->next = retNode;
	return retNode;
}
//链表尾插
void SLPushBack(SList* phead, SLDataType x)
{
	assert(phead);
	SList* Node = ListBuyNode(x);
	Node->prve = phead->prve;
	Node->next = phead;
	phead->prve->next = Node;
	phead->prve = Node;
}
//链表数据显示
void SLPrint(SList* phead)
{
	assert(phead);
	SList* pcur = phead->next;
	while (pcur != phead)//哨兵位作为结束标识
	{
		printf("%d", pcur->data);
		if (pcur->next != phead)
			printf("->");
		pcur = pcur->next;
	}
	printf("\n");
}
//链表头插
void SLPustFront(SList* phead, SLDataType x)
{
	assert(phead);
	SList* Node = ListBuyNode(x);
	Node->next = phead->next;
	Node->prve = phead;
	phead->next->prve = Node;
	phead->next = Node;
}
//链表尾删
void SLPopBack(SList* phead)
{
	assert(phead);
	assert(phead->next != phead);
	SList* del = phead->prve;
	del->prve->next = phead;
	phead->prve = del->prve;
	free(del);
	del = NULL;
}
//链表头删
void SLPopFront(SList* phead)
{
	assert(phead);
	assert(phead->next != phead);
	SList* del = phead->next;
	del->next->prve = phead;
	phead->next = del->next;
	free(del);
	del = NULL;
}
//指定位置插入
void SLInsert(SList* pos, SLDataType x)
{
	assert(pos);
	SList* Node = ListBuyNode(x);
	Node->next = pos->next;
	Node->prve = pos;
	pos->next = Node;
	Node->next->prve = Node;
}
//查找节点
SList* SLfind(SList* phead, SLDataType x)
{
	assert(phead);
	SList* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}
//指定位置删除
void SLEarse(SList* pos)
{
	assert(pos);
	pos->prve->next = pos->next;
	pos->next->prve = pos->prve;
	free(pos);
	pos = NULL;
}
//链表销毁
void SLDestory(SList** pphead)
{
	assert(pphead && *pphead);
	SList* pcur = (*pphead)->next;
	while (pcur != *pphead)
	{
		SList* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(*pphead);
	*pphead = NULL;
}

test.c 函数的调用:

#include "List.h"

void SLtest()
{
	SList* plist = NULL;
	plist = SLInit();
	//尾插
	SLPushBack(plist, 1);
	SLPushBack(plist, 2);
	SLPushBack(plist, 3);
	SLPushBack(plist, 4);
	SLPrint(plist);//打印:1->2->3->4
	//头插
	SLPustFront(plist, 5);
	SLPustFront(plist, 6);
	SLPustFront(plist, 7);
	SLPrint(plist);//打印:7->6->5->1->2->3->4
	//尾删
	SLPopBack(plist);
	SLPrint(plist);//打印:7->6->5->1->2->3
	//头删
	SLPopFront(plist);
	SLPrint(plist);//打印:6->5->1->2->3
	//指定位置插入
	SList* find = SLfind(plist, 5);
	SLInsert(find, 11);
	SLPrint(plist);//打印:6->5->11->1->2->3
	//指定位置删除
	find = SLfind(plist, 1);
	SLEarse(find);
	SLPrint(plist);//打印:6->5->11->2->3
	//链表销毁
	SLDestory(&plist);
}
int main()
{
	SLtest();
	return 0;
}

3、顺序表和双向链表的优缺点分析

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持O(N)
任意位置插入或者删除元素可能需要搬移元素,效率低O(N)只需要修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁

数据结构第3篇笔记到这里也就结束了,我们下一篇笔记再见-

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

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

相关文章

第TR1---TR3周: Pytorch复现Transformer

TR1 一、文本输入处理 1. 词向量 和常见的NLP 任务一样&#xff0c;首先会使用词嵌入算法&#xff08;embedding algorithm&#xff09;&#xff0c;将输入文本序列的每个词转换为一个词向量。 如下图所示&#xff0c;假设我们的输入文本是序列包含了3个词&#xff0c;那么每…

2025深圳国际人工智能展览会

2025深圳国际人工智能展览会 Shenzhen International Artificial Intelligence Exhibition 2025 时间&#xff1a;2025年6月25-27日 地点&#xff1a;深圳国际会展中心&#xff08;宝安新馆&#xff09; 详询主办方陆先生 I38&#xff08;前三位&#xff09; I82I&#…

Linux系统部署MongoDB开源文档型数据库并实现无公网IP远程访问

文章目录 前言1. 安装Docker2. 使用Docker拉取MongoDB镜像3. 创建并启动MongoDB容器4. 本地连接测试5. 公网远程访问本地MongoDB容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 &#x1f4a1; 推荐 前些天发现了一个巨牛的人工智能学习网站&am…

chrome 谷歌浏览器插件打包

1、找到id对应的字符串去搜索 C:\Users\<你的用户名>\AppData\Local\Google\Chrome\User Data\Default\Extensions2、选择根目录 直接加载下面的路径扩展可用&#xff1a;

【探索Linux】P.37(传输层 —— TCP协议通信机制 | 确认应答(ACK)机制 | 超时重传机制)

阅读导航 引言一、确认应答(ACK)机制1. 成功接收2. 过程中存在丢包3. 引入序列号&#xff08;1&#xff09;序列号的定义&#xff08;2&#xff09;序列号的作用&#xff08;3&#xff09;序列号的工作原理&#xff08;4&#xff09;序列号和确认应答号 二、超时重传机制1. 超时…

【操作与配置】Linux的CPU深度学习环境

Conda安装 可浏览官网&#xff1a;Miniconda — Anaconda 文档 这四条命令会快速而且悄悄地安装最新的64位安装程序&#xff0c;然后清理安装过程中产生的文件。如果需要安装 Linux 版本的其他版本或架构的 Miniconda&#xff0c;只需要在命令中更改安装程序的名称。 mkdir …

隐私计算实训营第二期第十课:基于SPU机器学习建模实践

隐私计算实训营第二期-第十课 第十课&#xff1a;基于SPU机器学习建模实践1 隐私保护机器学习背景1.1 机器学习中隐私保护的需求1.2 PPML提供的技术解决方案 2 SPU架构2.1 SPU前端2.2 SPU编译器2.3 SPU运行时2.4 SPU目标 3 密态训练与推理3.1 四个基本问题3.2 解决数据来源问题…

二叉搜索树(BST)

目录 一、概念 二、代码实现 1.框架 2.查找 3.插入 4.删除 5.递归的写法 三、应用 一、概念 二、代码实现 1.框架 #pragma oncenamespace utoKey {//结点template<class K>struct BinarySearchTreeNode{//结点的typedeftypedef BinarySearchTreeNode Node;//Nod…

利用pg_rman进行备份与恢复操作

文章目录 pg_rman简介一、安装配置pg_rman二、创建表与用户三、备份与恢复 pg_rman简介 pg_rman 是 PostgreSQL 的在线备份和恢复工具。类似oracle 的 rman pg_rman 项目的目标是提供一种与 pg_dump 一样简单的在线备份和 PITR 方法。此外&#xff0c;它还为每个数据库集群维护…

AIGC时代,“人”的核心价值在何处?

随着科技的浪潮汹涌向前&#xff0c;人工智能生成内容&#xff08;AIGC&#xff09;已悄然渗透至我们生活的每一个角落&#xff0c;从创意设计到信息传播&#xff0c;其影响力与变革力愈发显著。在这一由算法驱动的新纪元里&#xff0c;人类社会运作模式、学习途径及职业形态均…

眼动追踪技术 | 眼动的分类和模型

摘要 灵长类动物用于调整中央凹位置的正常眼动&#xff0c;几乎都可以归结为五种基本类型的组合&#xff1a;扫视、平稳追踪、聚散、前庭眼震和生理性眼震(与注视相关的微小运动)。聚散运动用于将双眼聚焦于远处的目标(深度知觉)。其他运动(如适应和聚焦)指的是眼动的非位置变…

LMT加仿真,十一届大唐杯全国总决赛

这次省赛带了太多个省一了&#xff0c;并且很多都进入了国赛总决赛&#xff0c;具体可看下面的图片&#xff0c;只放了一部分。目前只有B组是只有一个商用设备赛也就是LMT&#xff0c;A组和高职组都是仿真实践赛加上商用设备赛。 针对商用设备赛有对应的资料&#xff…

【深度学习】第3章——回归模型与求解分析

一、回归分析 1.定义 分析自变量与因变量之间定量的因果关系&#xff0c;根据已有的数据拟合出变量之间的关系。 2.回归和分类的区别和联系 3.线性模型 4.非线性模型 5.线性回归※ 面对回归问题&#xff0c;通常分三步解决 第一步&#xff1a;选定使用的model&#xff0c;…

CFS三层内网渗透——第二层内网打点并拿下第三层内网(三)

目录 八哥cms的后台历史漏洞 配置socks代理 ​以我的kali为例,手动添加 socks配置好了&#xff0c;直接sqlmap跑 ​登录进后台 蚁剑配置socks代理 ​ 测试连接 ​编辑 成功上线 上传正向后门 生成正向后门 上传后门 ​内网信息收集 ​进入目标二内网机器&#xf…

SAP-SD同一物料下单价格确不同

业务说明&#xff1a; 业务部门反馈&#xff0c;同一物料下销售订单时&#xff0c;价格确不同。 那么这个价格是怎么取到的呢&#xff1f; 逻辑说明&#xff1a; 1、首先查看销售订单 可以看到相同物料价格是不同的&#xff0c;条件类型都是ZPR5&#xff0c;但是客户是不同…

相关款式1111

一、花梨木迎客松 1. 风速打单 发现只有在兄弟店铺有售卖 六月份成交订单数有62笔 2. 生意参谋 兄弟店铺商品访客数&#xff1a;3548&#xff0c;支付件数&#xff1a;95件 二. 竹节茶刷&#xff08;引流&#xff09; 1. 风速打单 六月订单数有165笔 兄弟&#xff1a;…

揭秘数据之美:【Seaborn】在现代【数学建模】中的革命性应用

目录 已知数据集 tips 生成数据集并保存为CSV文件 数据预览&#xff1a; 导入和预览数据 步骤1&#xff1a;绘制散点图&#xff08;Scatter Plot&#xff09; 步骤2&#xff1a;添加回归线&#xff08;Regression Analysis&#xff09; 步骤3&#xff1a;分类变量分析&…

Mall,正在和年轻人重新对话

【潮汐商业评论/原创】 结束了一下午的苦闷培训&#xff0c;当Cindy赶到重庆十字大道时&#xff0c;才发现十字路口上的巨大“飞行棋”在前两天就已经撤展了。 “来了又错过&#xff0c;就会觉得遗憾&#xff0c;毕竟这样的路口不多&#xff0c;展陈又不可能会返场。” 飞行棋…

藏文作文写作业推荐什么学习工具?《藏文翻译词典》App值得你使用,一款好用准确的藏语词汇查询辞典!

探索藏语的奥秘&#xff0c;体验藏族文化的魅力&#xff0c;尽在《藏文翻译词典》App。这款App是藏汉翻译的神器&#xff0c;也是藏语学习者的必备工具。在学习过程中遇到不会的藏语单词&#xff0c;可以使用《藏文翻译词典》App进行查询&#xff01; 主要特性&#xff1a; 藏…

SCT612404通道,高效高集成,摄像头模组电源集成芯片

集成三路降压变换器&#xff0c;1CH高压BUCK,2CH低压Buck >HVBuck1:输入电压4.0V-20V,输出电流1.2A,Voo300mV/500mV >LVBuck2:输入电压2.7V-5V,输出电流0.6A , 固定1.8V输出 ;LVBuck3:输λ2.7V-5V,输出电流1.2A,可设定固定输出&#xff1a; 1 . 1 V / 1 . 2 V / 1 . 3 …