【数据结构】顺序表和链表详解(下)

news2025/6/9 9:52:09

前言:上期我们从顺序表开始讲到了单链表的概念,分类,和实现,而这期我们来将相较于单链表没那么常用的双向链表。

在这里插入图片描述

文章目录

  • 一、双向链表
  • 二,双向链表的实现
    • 一,增
      • 1,头插
      • 2,尾插
      • 3,在指定位置后插入
    • 二,删
      • 1,头删
      • 2,尾删
      • 3,删除pos位置的节点
    • 三,查
    • 四,链表的销毁
    • 五,测试代码
    • 六,顺序表与链表的总结

一、双向链表

前面我们讲过单链表,我们知道他是单向不带头,不循环链表结构,而双向链表就不一样了,双向链表是:带头的双向循环链表。

前面我们将链表的分类,但没有具体展现每一种链表到底是什么样的;是因为怕大家混淆,当时为了方便理解就把单链表的第一个节点当作是头节点;但实际上单链表是每一头节点的。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
既然双链表是双向带头的循环链表,那么将上面这几种结构结合起来自然就得到了双链表的结构:
在这里插入图片描述

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

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

二,双向链表的实现

首先要实现双链表我们就要创建一个双链表与单链表的实现一样。
在.h文件中

//需要包含的头文件
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int LTData;
//双向链表的构建
typedef struct ListNode
{
	LTData data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

构建好双链表后,我们可以发现双链表比单链表多了一个prev指针,指向它的前一个节点。这一点与单链表很不一样,仅仅多了一个prev指针就使得了链表变得循环起来。而且在单链表中是不能往前遍历的,但在双链表中就可以。

在这里插入图片描述

紧接着我们初始化节点:

//初始化新节点
LTNode* LTNodeInit();
//封装一个开辟一个结点的函数   
LTNode* LTBuyNode(LTData x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		//到这说明开辟失败
		perror("malloc fail!");
		exit(1);
	}
	//到这说明开辟成功 调节新结点的prev和head指针 指向自己 因为时双向链表
	newnode->prev = newnode;
	newnode->next = newnode;
	newnode->data = x;
	//调整完后返回新结点的地址
	return newnode;
}

//初始化
LTNode* LTNodeInit()
{
	LTNode* phead = LTBuyNode(-1);//向内存申请一块空间 代表一个头结点 
	return phead;//返回头结点的地址
}

在这里插入图片描述
创建好双链表,初始化节点后我们就可以来实现双链表的增,删,查,改了。

一,增

1,头插

在这里插入图片描述

//头插
void LTPushFront(LTNode* phead, LTData x);
//头插
void LTPushFront(LTNode* phead, LTData x)
{
	assert(phead);
	//既然要插入结点 就要先创建一个结点
	LTNode* newnode = LTBuyNode(x);

	//head newnode head->next(d1) 处理他们三个的指向关系
	newnode->next = phead->next;
	newnode->prev = phead;
	
	phead->next->prev = newnode;
	phead->next = newnode;
}

2,尾插

在这里插入图片描述

//头结点不发生改变传一级指针
//头结点要发生改变传二级指针
//尾插
void LTPushBack(LTNode* phead,LTData x);

//尾插
void LTPushBack(LTNode* phead, LTData x)
{
	assert(phead);
	//既然要尾插那么就要有新的结点 新的结点使用LTBuyNode方法开辟 然后返回新结点的地址
	LTNode* newnode = LTBuyNode(x);

	//phead  phead->prev(尾结点) newnode
	
	//先修改newnode
	newnode->prev = phead->prev;//phead->prev即d3结点 newnode指向d3
	newnode->next = phead;//尾与头相连

	//再修改链表内部的结点
	phead->prev->next = newnode;//相当于d3->newnode d3指向newnode
	phead->prev = newnode;//头与尾相连
}

3,在指定位置后插入

在这里插入图片描述

//在指定位置之后插入结点
void LTInsert(LTNode* pos, LTData x);
//在指定位置之后插入结点
void LTInsert(LTNode* pos, LTData x)
{
	assert(pos);
	LTNode* newnode = LTBuyNode(x);
	
	//pos newnode pos->next
	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}

二,删

1,头删

在这里插入图片描述

//头删
void LTPopFront(LTNode* phead);
//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	LTNode* del = phead->next;//phead->next 为头结点
	//处理 head del del->nex
	phead->next = del->next;
	del->next->prev = phead;

	//释放头结点
	free(del);
	del = NULL;
}

2,尾删

在这里插入图片描述

//尾删
void LTPopBack(LTNode* phead);   
//尾删
void LTPopBack(LTNode* phead)
{
	//先判断是否是空链表
	assert(!LTEmpty(phead));//如果链表为空 返回1 !1(非1) 就为0 就会断言报错
	LTNode* del = phead->prev;//定义del来保存尾结点
	//处理head  head-next->prev(del->prev)     head->next(del)
	//             尾结点的上一个结点                   尾结点

	del->prev->next = phead;
	phead->prev = del->prev;
	 
	//释放
	free(del);
	del = NULL;
}

3,删除pos位置的节点

在这里插入图片描述

//删除pos位置的结点
void LTErase(LTNode* pos);
//删除pos位置的结点
void LTErase(LTNode* pos)
{
	assert(pos);
	//pos->prev pos pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

三,查

在这里插入图片描述

//查找
LTNode* LTFind(LTNode* phead, LTData x);
//查找
LTNode* LTFind(LTNode* phead, LTData x)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//走到这里已经循环完了链表都没找到 说明找不到了返回NULL
	return NULL;
}

四,链表的销毁

与单链表一样,双链表也是我们人为向操作系统申请的空间,为了避免空间浪费再我们不使用链表的时候就将他销毁,还给操作系统。
在这里插入图片描述

//链表的销毁
void LTdesTroy(LTNode*phead);
void LTdesTroy(LTNode*phead)
{
	LTNode*pcur=phead->next;
	while(pcur!=NULL)
	{
		LTNode*next=pcur->next;
		free(pcur);
		pcur=next;
	}
	//跳出循环只剩下哨兵位 释放哨兵位
	free(phead);
	phead=NULL;
}

五,测试代码

以上就是双链表的实现,实现完后我们可以用一些测试代码来测试一下,

//链表的打印
//链表的打印
void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}
void test1()
{
	
	LTNode* plist= LTNodeInit();//ʼ
	//β
	/*LTPushBack(plist,1);
	LTPushBack(plist,2);
	LTPushBack(plist,3);
	LTPushBack(plist,4);*/

	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPrint(plist);
}

int main()
{
	test1();
	return 0;
}

六,顺序表与链表的总结

以上就是所有链表和顺序表的内容了,我们最后再来比较一下二者的区别:
在这里插入图片描述
以上就是本章的全部内容啦!
最后感谢能够看到这里的读者,如果我的文章能够帮到你那我甚是荣幸,文章有任何问题都欢迎指出!制作不易还望给一个免费的三连,你们的支持就是我最大的动力!
在这里插入图片描述

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

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

相关文章

【系统架构设计师】绪论-系统架构概述

目录 绪论 系统架构概述 单选题 绪论 系统架构概述 单选题 1、软件方法学是以软件开发方法为研究对象的学科。其中&#xff0c;&#xff08;&#xff09;是先对最高居次中的问题进行定义、设计、编程和测试&#xff0c;而将其中未解决的问题作为一个子任务放到下一层次中去…

SQL-事务(2025.6.6-2025.6.7学习篇)

1、简介 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 默认MySQL的事务是自动提交的&#xff0c;也就是说&#xff0…

Virtex II 系列FPGA的配置原理

对FPGA 芯片的配置&#xff0c;本质上是将根据设计生成的包含配置命令和配置数据的比特流文件写入到配置存储器中。 1 配置模式 Virtex II 系列FPGA 一共有五种配置模式&#xff0c;配置模式的选择是根据管脚M[2:0]来决定。 &#xff08;1&#xff09;串行配置模式 串行配置模…

蓝桥杯 国赛2024python(b组)题目(1-3)

第一题 试卷答题页 - 蓝桥云课 问题描述 在今年蓝桥杯的决赛中&#xff0c;一共有 1010 道题目&#xff0c;每道题目的分数依次为 55 分&#xff0c;55 分&#xff0c;1010 分&#xff0c;1010 分&#xff0c;1515 分&#xff0c;1515 分&#xff0c;2020 分&#xff0c;2020 分…

算法题(165):汉诺塔问题

审题&#xff1a; 本题需要我们找到最优的汉诺塔搬法然后将移动路径输出 思路&#xff1a; 方法一&#xff1a;递归 我们先分析题目 n为2的情况&#xff0c;我们先将第一个盘子移动到三号柱子上&#xff0c;然后再将二号盘子移动到二号柱子上 n为3的情况&#xff0c;我们先将前…

玄机——某次行业攻防应急响应(带镜像)

今天给大家带来一次攻防实战演练复现的过程。 文章目录 简介靶机简介1.根据流量包分析首个进行扫描攻击的IP是2.根据流量包分析第二个扫描攻击的IP和漏扫工具&#xff0c;以flag{x.x.x.x&工具名}3.提交频繁爆破密钥的IP及爆破次数&#xff0c;以flag{ip&次数}提交4. 提…

低代码逻辑引擎配置化实战:三步穿透审批记录查询

在堆积如山的报销单中埋头寻找某笔特殊费用的审批轨迹在跨部门协作时被追问"这个合同到底卡在哪个环节" 在快节奏的办公自动化场景中&#xff0c;这些场景是很常见的&#xff0c;传统OA系统中分散的审批记录查询方式往往太繁琐。 为破解这一痛点&#xff0c;在JVS低…

【Zephyr 系列 15】构建企业级 BLE 模块通用框架:驱动 + 事件 + 状态机 + 低功耗全栈设计

🧠关键词:Zephyr、BLE 模块、架构设计、驱动封装、事件机制、状态机、低功耗、可维护框架 📌面向读者:希望将 BLE 项目从“Demo 工程”升级为“企业可复用框架”的研发人员与技术负责人 📊预计字数:5500+ 字 🧭 前言:从 Demo 到产品化,架构该如何升级? 多数 BLE…

Docker构建Vite项目内存溢出:从Heap Limit报错到完美解决的剖析

问题现象:诡异的"消失的index.html" 最近在CI/CD流水线中遇到诡异现象:使用Docker构建Vite项目时,dist目录中缺少关键的index.html文件,但本地构建完全正常。报错截图显示关键信息: FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out…

Android端口转发

如上图所示&#xff0c;有一个Android设备&#xff0c;Android设备里面有主板&#xff0c;主板上有网络接口和Wi-Fi&#xff0c;网络接口通过网线连接了一个网络摄像头&#xff0c;这就跟电脑一样&#xff0c;电脑即可以通过网线接入一个网络&#xff0c;也可以同时用Wi-Fi接入…

PHP环境极速搭建

一、为什么选择phpStudy VS Code&#xff1f; 作为一名初次接触PHP的开发者&#xff0c;我深知环境配置往往是学习路上的第一道门槛。传统PHP环境搭建需要手动配置Apache/Nginx、PHP解释器、MySQL等多重组件&#xff0c;光是处理版本兼容性和依赖问题就可能耗费半天时间——这…

建造者模式深度解析与实战应用

作者简介 我是摘星&#xff0c;一名全栈开发者&#xff0c;专注 Java后端开发、AI工程化 与 云计算架构 领域&#xff0c;擅长Python技术栈。热衷于探索前沿技术&#xff0c;包括大模型应用、云原生解决方案及自动化工具开发。日常深耕技术实践&#xff0c;乐于分享实战经验与…

代码中文抽取工具并替换工具(以ts为例)

文章目录 基本思路目录结构配置文件AST解析替换代码中文生成Excel启动脚本 基本思路 通过对应语言的AST解析出中文相关信息&#xff08;文件、所在行列等&#xff09;存到临时文件通过相关信息&#xff0c;逐个文件位置替换掉中文基于临时文件&#xff0c;通过py脚本生成Excel…

pgsql batch insert optimization (reWriteBatchedInserts )

reWriteBatchedInserts 是 PostgreSQL JDBC 驱动 提供的一个优化选项&#xff0c;它可以 重写批量插入语句&#xff0c;从而提高插入性能。 作用 当 reWriteBatchedInsertstrue 时&#xff0c;PostgreSQL JDBC 驱动会将 多个单独的 INSERT 语句 转换为 一个多行 INSERT 语句&a…

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(上)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…

华为云Flexus+DeepSeek征文 | 基于DeepSeek-V3构建企业知识库问答机器人实战

作者简介 我是摘星&#xff0c;一名专注于云计算和AI技术的开发者。本次通过华为云MaaS平台体验DeepSeek系列模型&#xff0c;将实际使用经验分享给大家&#xff0c;希望能帮助开发者快速掌握华为云AI服务的核心能力。 目录 作者简介 1. 引言 2. 技术选型与架构设计 2.1 技…

【Docker 01】Docker 简介

&#x1f308; 一、虚拟化、容器化 ⭐ 1. 什么是虚拟化、容器化 物理机&#xff1a;真实存在的服务器 / 计算机&#xff0c;对于虚拟机来说&#xff0c;物理机为虚拟机提供了硬件环境。虚拟化&#xff1a;通过虚拟化技术将一台计算机虚拟为 1 ~ n 台逻辑计算机。在一台计算机…

AUTOSAR实战教程--DoIP_02_诊断链路建立流程

第一步&#xff1a;DoIP实体车辆声明/诊断仪车辆识别请求 打开激活线以后&#xff0c;DoIP实体发的三帧车辆声明报文。其中包含了DoIP实体的诊断逻辑地址&#xff08;可以类比DoCAN的物理请求/响应地址&#xff09;&#xff0c;对应车辆的VIN码&#xff08;若已配置&#xff0…

音频剪辑软件少之又少好用

我们平时见到的图片以及视频编辑工具非常多&#xff0c;但是音频剪辑软件却是少之又少&#xff0c;更不用说有没有好用的&#xff0c;今天&#xff0c;给大家带来一款非常专业的音频剪辑软件&#xff0c;而且是会员喔。 软件简介 一款手机号登录即可以享受会员的超专业音频剪…

客户端和服务器已成功建立 TCP 连接【输出解析】

文章目录 图片**1. 连接状态解析****第一条记录&#xff08;服务器监听&#xff09;****第二条记录&#xff08;客户端 → 服务器&#xff09;****第三条记录&#xff08;服务器 → 客户端&#xff09;** **2. 关键概念澄清****(1) 0.0.0.0 的含义****(2) 端口号的分配规则** *…