数据结构双向链表

Hello,好久不见,今天我们讲链表的双向链表,这是一个很厉害的链表,带头双向且循环,学了这个链表,你会发现顺序表的头插头删不再是一个麻烦问题,单链表的尾插尾删也变得简单起来了,那废话不多说,让我们开始我们的学习吧!
首先我们要了解它的物理和逻辑结构,那他们的样子是怎样的呢,首先是一个带头的,那这个难道是我们的哨兵位吗,又是双向,且循环,那让我们来画图了解它吧。

大致就是这样的一个形状,那我们现在需要这样的一个结构体来实现这样的功能,首先应该有一个存储数据的,就是data,然后就是得有两个指针,一个指向前面的节点,一个就是指向后面的节点,那我们就叫它们一个pre指向前面,一个next指向后面,我们来实现一下吧。
typedef int DListType;
typedef struct DList
{struct DList* pre;struct DList* next;DListType data;}DLNode;
为了方便我们写后面的时候结构体方便一点,我们先定义结构体为DLNode,这样更加方便使用。
现在我们要实现下面的各种接口来完成双链表
首先最重要的就是怎么初始化
初始化的话我们先要想想这个接口函数改的参数和返回值
因为是双向链表,所以得有一个head的头节点,这样才能链接后面的内容
初始化双链表
DLNode* DListInit();
接口函数的名字
这里我们分析首先我们得返回值为什么不是void,而是DLNode*
因为我们要在这里面创建一个头节点,这个节点我们后面都得使用,其次还有一个原因就是这样头节点就不会被销毁,当然我们也可以在外面创建一个节点,然后我们在传它的指针进去,对结构体的内容进行修改,都可以达到相同的作用,废话不多说,我们来实现吧!
DLNode* DListInit()
{DLNode* pHead = (DLNode*)malloc(sizeof(DLNode));assert(pHead);pHead->next = pHead;pHead->pre = pHead;
}
其实很简单,这里必须指针指向自己才可以,如果不这样的话,那我们的循环就不能实现了。
接下来就是怎么打印,打印很简单,我们将它这个指针传过去就行了。
打印双链表
void DListPrint(DLNode* pHead)
{assert(pHead);DLNode* cur = pHead->next;while (cur != pHead){printf("%d ", cur->data);cur = cur->next;}printf("\n");
}
打印函数的想法就是我们找head后面的那个节点,然后打印,因为头节点我们不打印,所以取cur开始,因为是循环,所以cur最后会等于pHead,这样的话就是一个循环,所以我们找下一个就行了,然后打印后更新cur。
接下来我们要创建一个节点,这样才能链接起来我们的节点。
DLNode* CreatNewNode(DListType x)
DLNode* CreatNewNode(DListType x)
{DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));assert(newnode);newnode->data = x;newnode->next = NULL;newnode->pre = NULL;return newnode;
}
创造节点好了,接下来就是尾插一个节点进去,我们前面单链表尾插首先要找到尾的位置,然后再去尾插,同时要先找到尾的前一个位置,然后free尾,这样时间复杂度就是O(N),所以效率不是特别高,那我们现在因为head的位置里存放的就是尾的位置,所以不用进行查找了。我们现在来实现一下吧
双链表尾插
void DListPushBACK(DLNode* pHead, DListType x)
{assert(pHead);DLNode* newnode = CreatNewNode(x);DLNode* pre = pHead->pre;pHead->pre = newnode;newnode->next = pHead;pre->next = newnode;newnode->pre = pre;
}
其实尾插也很简单,我们这里先用一个指针保存位置,这样的话下一次插入就可以找到尾的位置,而且还不用注重顺序,这样大大的提升效率
有了尾插那就肯定有头插,一样的办法我们来实现一下
双链表的头插
void DListPushFront(DLNode* pHead, DListType x)
{assert(pHead);DLNode* newnode = CreatNewNode(x);DLNode* next = pHead->next;pHead->next = newnode;newnode->pre = pHead;newnode->next = next;next->pre = newnode;
}
有了头插这些,那肯定还有头删和尾删
那我们也来实现一下吧
尾删
void DListPopBack(DLNode* pHead)
{assert(pHead);if (pHead->next != pHead){DLNode* del = pHead->pre;del->pre->next = pHead;pHead->pre = del->pre;free(del);}
}
前面写的代码都需要测试一下,我们先来测试三个
#include"DList.h"
void test()
{DLNode* head = DListInit();DListPushBack(head, 1);DListPushBack(head, 2);DListPushBack(head, 3);DListPushBack(head, 4);DListPrint(head);DListPushFront(head, 111);DListPushFront(head, 111);DListPushFront(head, 111);DListPrint(head);DListPopBack(head);DListPopBack(head);DListPopBack(head);DListPrint(head);
}
int main()
{test();return 0;
}
发现我们的程序没有问题,我们再来实现一下头删
void DListPopFront(DLNode* pHead)
{assert(pHead);if (pHead->next != pHead){DLNode* del = pHead->next;pHead->next = del->next;del->next->pre = pHead;free(del);}
}
接下来就是双链表的查找,有了查找我们才能和在任意位置的删除和插入连用,那我们现在来实现一下吧
DLNode* DListFind(DLNode* pHead, DListType x)
{assert(pHead);DLNode* cur = pHead->next;while (cur != pHead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
随机插入
void DListInsert(DLNode* pos, DListType x)
{assrt(pos);DLNode* newnode = CreatNewNode(x);DLNode* pospre = pos->pre;pospre->next = newnode;newnode->pre = pospre;newnode->next = pos;pos->pre = newnode;}
也不是随机插入,是在pos位置之前插入,我们直接传pos和x就行,然后在根据之前的想法一步一步的进行插入链接就行
这里可以更新一下头插和尾插,这里就不演示了
那我们在写一个删除pos位置的函数
删除pos位置
void DListPop(DLNode* pos)
{assert(pos);DLNode* pospre = pos->pre;DLNode* posnext = pos->next;pospre->next = posnext;posnext->pre = pospre;free(pos);
}
还有一个destory我们开辟的空间,这个遍历一遍数组, 然后一个一个释放就行,但是会有问题,我们释放的时候必须看要机记住位置,可以从尾巴开始释放,用一个指针记住前面一个的位置,然后释放掉尾巴,然后更新前一个位置,用while控制一下就行了
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DListType;
typedef struct DList
{struct DList* pre;struct DList* next;DListType data;}DLNode;DLNode* DListInit();void DListPrint(DLNode* pHead);DLNode* CreatNewNode(DListType x);void DListPushBack(DLNode* pHead, DListType x);void DListPushFront(DLNode* pHead, DListType x);void DListPopBack(DLNode* pHead);void DListPopFront(DLNode* pHead);DLNode* DListFind(DLNode* pHead, DListType x);void DListInsert(DLNode* pos, DListType x);void DListPop(DLNode* pos);
#include"DList.h"DLNode* DListInit()
{DLNode* pHead = (DLNode*)malloc(sizeof(DLNode));pHead->next = pHead;pHead->pre = pHead;
}void DListPrint(DLNode* pHead)
{assert(pHead);DLNode* cur = pHead->next;while (cur != pHead){printf("%d ", cur->data);cur = cur->next;}printf("\n");
}DLNode* CreatNewNode(DListType x)
{DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));assert(newnode);newnode->data = x;newnode->next = NULL;newnode->pre = NULL;return newnode;
}void DListPushBack(DLNode* pHead, DListType x)
{DLNode* newnode = CreatNewNode(x);DLNode* pre = pHead->pre;pHead->pre = newnode;newnode->next = pHead;pre->next = newnode;newnode->pre = pre;
}void DListPushFront(DLNode* pHead, DListType x)
{DLNode* newnode = CreatNewNode(x);DLNode* next = pHead->next;pHead->next = newnode;newnode->pre = pHead;newnode->next = next;next->pre = newnode;
}void DListPopBack(DLNode* pHead)
{assert(pHead);if (pHead->next != pHead){DLNode* del = pHead->pre;del->pre->next = pHead;pHead->pre = del->pre;free(del);}
}void DListPopFront(DLNode* pHead)
{assert(pHead);if (pHead->next != pHead){DLNode* del = pHead->next;pHead->next = del->next;del->next->pre = pHead;free(del);}
}DLNode* DListFind(DLNode* pHead, DListType x)
{assert(pHead);DLNode* cur = pHead->next;while (cur != pHead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}void DListInsert(DLNode* pos, DListType x)
{assrt(pos);DLNode* newnode = CreatNewNode(x);DLNode* pospre = pos->pre;pospre->next = newnode;newnode->pre = pospre;newnode->next = pos;pos->pre = newnode;}void DListPop(DLNode* pos)
{assert(pos);DLNode* pospre = pos->pre;DLNode* posnext = pos->next;pospre->next = posnext;posnext->pre = pospre;free(pos);
}
谢谢大家,我们下期再见
相关文章:
数据结构双向链表
Hello,好久不见,今天我们讲链表的双向链表,这是一个很厉害的链表,带头双向且循环,学了这个链表,你会发现顺序表的头插头删不再是一个麻烦问题,单链表的尾插尾删也变得简单起来了,那废…...
解决政务审计大数据传输难题!镭速传输为政务行业提供解决方案
政务行业是国家治理的重要组成部分,涉及到国家安全、社会稳定、民生福祉等方面。随着信息技术的快速发展和革新,政务信息化也迎来了新一轮的升级浪潮。国家相继出台了《国家信息化发展战略纲要》《“十三五”国家信息化规划》《“十四五”推进国家政务信…...
redis 7高级篇1 redis的单线程与多线程
一 redis单线程与多线程 1.1 redis单线程&多线程 1.redis的单线程 redis单线程主要是指Redis的网络IO和键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理…...
GO语言:Worker Pools线程池、Select语句、Metex互斥锁详细示例教程
目录标题 一、Buffered Channels and Worker Pools1. Goroutine and Channel Example 线程和通道示例2. Deadlock 死锁3. Closing buffered channels 关闭通道4. Length vs Capacity 长度和容量5. WaitGroup6. Worker Pool Implementation 线程池 二、Select1. Example2. Defau…...
vue ui 创建项目没有反应
问题 cmd中输入 vue ui 没有反应 解决办法 vue ui命令需要vue3.0以上的版本才可以 1、查看当前版本 vue --version vue版本在3.0以下是没有ui命令的 2、查看版本所拥有的命令 vue -h 3、卸载之前版本的vue npm uninstall vue-cli -g 卸载完成,检查是否已经…...
go语言中channel类型
目录 一、什么是channel 二、为什么要有channel 三、channel操作使用 初始化 操作 单向channel 双向channel,可读可写 四、close下什么场景会出现panic 五、总结 一、什么是channel Channels are a typed conduit through which you can send and receive …...
基于STM32F1的电子罗盘HMC5883L角度测量
基于STM32F1的电子罗盘HMC5883L角度测量 参考 1. HMC5883L模块 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Axqqv48y-1692885921487)(…\img\HMC5883L.png)] 型号:GY-271使用芯片:HMCL5883L供电电源:3-5V通…...
Oracle解锁表、包、用户、杀会话、停job
Oracle解锁表、包、用户、杀会话、停job 一、创建包tzq_server_pkg二、授权给需要使用的用户log三、解锁表:执行存过unlock_table(schema_name, table_name)四、解锁包:执行存过unlock_package(schema_name, pkg_name)五、解锁用户:执行存过u…...
软考高级系统架构设计师系列论文九十九:论软件开发平台的选择和应用
软考高级系统架构设计师系列论文九十九:论软件开发平台的选择和应用 一、相关知识点二、摘要三、正文四、总结一、相关知识点 软考高级系统架构设计师系列之:面向构件的软件设计,构件平台与典型架构二、摘要 本文从一个行业MIS系统的开发实践,讨论了软件开发平台的选择和应…...
Redis Pub/Sub 指南
Redis 不仅仅是一个数据库,还可以作为支持发布和订阅(Pub/Sub)操作的消息代理。本文将使用 Navicat for Redis 简要概述 Redis 的 Pub/Sub 功能。 关于发布或订阅消息范式 Pub/Sub 是一种模式,发送者(广播者…...
Nest(2):Nest 应用目录结构和脚手架命令介绍
Nest 应用目录结构和脚手架命令介绍 在正式使用 NestJS 进行开发之前,先来了解下 Nest 应用的目录结构,和一些常用的脚本命令。 工程目录 下面是使用 nest/cli 创建的 Nest 项目的目录结构。 上篇文章中介绍了 src 目录以及目录下各个文件的作用。下面…...
【嵌入式】MKV31F512VLL12 微控制器 (MCU) 、Cyclone® IV E EP4CE10E22I8LN,FPGA-现场可编程门阵列芯片
1、MKV31F512VLL12 微控制器 (MCU) 是适用于BLDC、PMSM和ACIM电机控制应用的高性能解决方案。这些MCU采用运行频率为100MHz/120MHz、带数字信号处理 (DSP) 和浮点单元 (FPU) 的ARM Cortex-M4内核。KV3x MCU配备两个采样率高达1.2MS/s的16位ADC、多个控制定时器以及512KB闪存。 …...
矢量调制分析基础
前言 本文介绍VSA 的矢量调制分析和数字调制分析测量能力。某些扫频调谐频谱分析仪也能通过使用另外的数字无线专用软件来提供数字调制分析。然而,VSA 通常在调制格式和解调算法配置等方面提供更大的测量灵活性,并提供更多的数据结果和轨迹轨迹显示。本…...
ensp-Ipv6配置配置
ensp-Ipv6配置配置 📎ipv6.zip📎Ipv6 网络.docx...
java八股文面试[java基础]—— hashCode 与 equals 区别 == 与 equals的区别
两个对象的hashCode()相同时,equals()相等吗?_两个对象的hashcode一样,equal一样么_不想当个程序员的博客-CSDN博客 equals():比较的是非基本类型的数据的引用地址(即内存地址)是否相同,但是对于重写equal…...
Dubbo之PojoUtils源码分析
功能概述 PojoUtils是一个工具类,能够进行深度遍历,将简单类型与复杂类型的对象进行转换,在泛化调用时用到(在泛化调用中,主要将Pojo对象与Map对象进行相互转换) 功能分析 核心类PojoUtils分析 主要成员…...
【C++】—— C++11新特性之 “右值引用和移动语义”
前言: 本期,我们将要的介绍有关 C右值引用 的相关知识。对于本期知识内容,大家是必须要能够掌握的,在面试中是属于重点考察对象。 目录 (一)左值引用和右值引用 1、什么是左值?什么是左值引用…...
谈一谈redis脑裂
什么是redis脑裂 (1)一主多从架构中,主节点与客户端通信正常,主节点与哨兵、从节点连接异常,客户端仍正常写入数据 (2)哨兵判定主节点下线,重新选主 (3)原主…...
基于原生Servlet使用模板引擎Thymeleaf访问界面
我们常在Spring Boot项目中使用Thymeleaf模板引擎,今天突发奇想,尝试原生Servlet访问! 说做就做 搭建完整的WEB项目 其中的大部分依赖都是后续报错 追加进来的 导入依赖 thymeleaf-3.0.11.RELEASE.jar 第一次访问 访问地址: http://localhost:8080…...
【C语言】15-函数-1
1. 初步认识函数 通过前几章的学习,已经可以编写一些简单的 C 语言程序了,但是如果程序的功能比较多,规模比较大,把所有的程序代码都写在一个主函数(main函数)中,就会使主函数变得庞杂、头绪不清,使阅读和维护程序变得困难。此外,有时程序中要多次实现某一功能就需要…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
实战设计模式之模板方法模式
概述 模板方法模式定义了一个操作中的算法骨架,并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下,重新定义算法中的某些步骤。简单来说,就是在一个方法中定义了要执行的步骤顺序或算法框架,但允许子类…...
DeepSeek越强,Kimi越慌?
被DeepSeek吊打的Kimi,还有多少人在用? 去年,月之暗面创始人杨植麟别提有多风光了。90后清华学霸,国产大模型六小虎之一,手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水,单月光是投流就花费2个亿。 疯…...
用js实现常见排序算法
以下是几种常见排序算法的 JS实现,包括选择排序、冒泡排序、插入排序、快速排序和归并排序,以及每种算法的特点和复杂度分析 1. 选择排序(Selection Sort) 核心思想:每次从未排序部分选择最小元素,与未排…...
比特币:固若金汤的数字堡垒与它的四道防线
第一道防线:机密信函——无法破解的哈希加密 将每一笔比特币交易比作一封在堡垒内部传递的机密信函。 解释“哈希”(Hashing)就是一种军事级的加密术(SHA-256),能将信函内容(交易细节…...
