数据结构——带头双向循环链表
数据结构——带头双向循环链表
- 一、带头双向循环链表的定义
- 二、带头双向循环链表的实现
- 2.1初始化创建带头双向循环链表的节点
- 2.2申请新节点
- 2.3节点的初始化
- 2.4带头双向循环链表的尾插
- 2.5带头双向循环链表的头插
- 2.6判空函数
- 2.7带头双向循环链表的打印函数
- 2.8带头双向循环链表的尾删
- 2.9带头双向循环链表的头删
- 2.11带头双向循环链表的在pos之前插入
- 2.12带头双向循环链表的在pos位置删除
- 2.14带头双向循环链表的销毁
- 三、完整代码
- 3.1LIst.h
- 3.2List.c
- 3.3test.c
一、带头双向循环链表的定义
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势。
带头双向循环链表包括一个带有哨兵位的头节点,该节点既可以作为链表的第一个节点,也可以作为链表的最后一个节点.
这种链表的特点是每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点,这样就可以实现双向遍历。
同时,链表的最后一个节点的后继指针指向头节点,形成了循环的结构。这样,我们可以在任意一个节点上进行前后移动,插入和删除操作,而不需要像单链表那样遍历整个链表去找到前一个节点。
需要注意的是,带头双向循环链表为空并不意味着没有一个节点,而是只有一个带哨兵位的头节点,所以在使用之前需要对链表进行初始化。
二、带头双向循环链表的实现
2.1初始化创建带头双向循环链表的节点
typedef struct ListNode
{Listdatatype data;struct ListNode* next;struct ListNode* prev;
}LTNode;

在创建带头双向循环链表的节点中比之前单链表节点的创建多了一个struct ListNode* prev;结构体指针,目的在与存储前一个节点的地址,便于将整个链表连在一起。
2.2申请新节点
//创建新节点
LTNode* BuyLTNode(Listdatatype x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}
动态申请内存结点,函数返回的是一个指针类型,用malloc开辟一个LTNode大小的空间,并用node指向这个空间,再判断是否为空,如为空就perror,显示错误信息。反之则把需要存储的数据x存到newnode指向的空间里面,并且把newnode->next,newnode->prev置为空。
2.3节点的初始化
LTNode* LTInit()
{LTNode* phead = BuyLTNode(-1);phead->next = phead;phead->prev = phead;return phead;
}
通过动态内存申请节点,申请了一个头节点。并且将它的phead->next ,phead->prev 都置为phead,得到如下图的头节点。

2.4带头双向循环链表的尾插
void LTPushBack(LTNode* phead, Listdatatype x)
{assert(phead);LTNode* tail = phead->prev;LTNode* newnode = BuyLTNode(x);tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}
尾插节点的方法:首先通过内存申请一个节点, 然后改变四个指针的指向,便可以完成带头双向循环链表的尾插。


2.5带头双向循环链表的头插
void LTFrontBack(LTNode* phead, Listdatatype x)
{assert(phead);LTNode* newnode = BuyLTNode(x);newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;newnode->prev = phead;
}


2.6判空函数
bool LTEmpty(LTNode* phead)
{assert(phead);return phead->next == phead;
}
2.7带头双向循环链表的打印函数
//打印
void LTPrint(LTNode* phead)
{LTNode* cur = phead->next;printf("guard<->");while (cur != phead){printf("%d<->", cur->data);cur = cur->next;}printf("\n");
}
2.8带头双向循环链表的尾删
//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(! LTEmpty(phead));LTNode* tail = phead->prev;LTNode* tailprev = tail->prev;//改变指针的指向free(tail);tailprev->next = phead;phead->prev = tailprev;
}

2.9带头双向循环链表的头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));LTNode* first = phead->next;LTNode* firstnext = first->next;free(first);phead->next = firstnext;firstnext->prev = phead;
}

2.11带头双向循环链表的在pos之前插入
void LTInsert(LTNode* pos, Listdatatype x)
{assert(pos);LTNode* newnode = BuyLTNode(x);LTNode* posprev = pos->prev;posprev->next = newnode;newnode->prev = posprev;newnode->next = pos;pos->prev = newnode;
}

2.12带头双向循环链表的在pos位置删除
void LTErase(LTNode* pos)
{assert(pos);LTNode* posprev = pos->prev;LTNode* posnext = pos->next;posprev->next = posnext;posnext->prev = posprev;free(pos);
}

2.14带头双向循环链表的销毁
//销毁
LTNode* LTDestory(LTNode* phead)
{LTNode* cur = phead->next;while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);
}
三、完整代码
3.1LIst.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int Listdatatype;
typedef struct ListNode
{Listdatatype data;struct ListNode* next;struct ListNode* prev;
}LTNode;//初始化
LTNode* LTInit();//尾插
void LTPushBack(LTNode* phead, Listdatatype x);//尾删
void LTPopBack(LTNode* phead);//头插
void LTFrontBack(LTNode* phead, Listdatatype x);//头删
void LTPopFront(LTNode* phead);//打印
void LTPrint(LTNode* phead);//判空
bool LTEmpty(LTNode* phead);//在pos之前插入
void LTInsert(LTNode* pos, Listdatatype x);//在pos之前删除
void LTErase(LTNode* pos);//寻找
LTNode* LTFind(LTNode* phead, Listdatatype x);//销毁
LTNode* LTDestory(LTNode* phead);
3.2List.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//创建新节点
LTNode* BuyLTNode(Listdatatype x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}LTNode* LTInit()
{LTNode* phead = BuyLTNode(-1);phead->next = phead;phead->prev = phead;return phead;
}
//尾插
void LTPushBack(LTNode* phead, Listdatatype x)
{assert(phead);LTNode* tail = phead->prev;LTNode* newnode = BuyLTNode(x);tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}//头插
void LTFrontBack(LTNode* phead, Listdatatype x)
{assert(phead);LTNode* newnode = BuyLTNode(x);newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;newnode->prev = phead;
}//判空
bool LTEmpty(LTNode* phead)
{assert(phead);return phead->next == phead;
}//打印
void LTPrint(LTNode* phead)
{LTNode* cur = phead->next;printf("guard<->");while (cur != phead){printf("%d<->", cur->data);cur = cur->next;}printf("\n");
}//尾删
void LTPopBack(LTNode* phead)
{assert(phead);assert(! LTEmpty(phead));LTNode* tail = phead->prev;LTNode* tailprev = tail->prev;//改变指针的指向free(tail);tailprev->next = phead;phead->prev = tailprev;
}//头删
void LTPopFront(LTNode* phead)
{assert(phead);assert(!LTEmpty(phead));LTNode* first = phead->next;LTNode* firstnext = first->next;free(first);phead->next = firstnext;firstnext->prev = phead;
}//寻找
LTNode* LTFind(LTNode* phead, Listdatatype x)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}//在pos之前插入
void LTInsert(LTNode* pos, Listdatatype x)
{assert(pos);LTNode* newnode = BuyLTNode(x);LTNode* posprev = pos->prev;posprev->next = newnode;newnode->prev = posprev;newnode->next = pos;pos->prev = newnode;
}在pos之前删除
//void LTErase(LTNode* pos)
//{
// assert(pos);
// LTNode* posprev = pos->prev;
// free(posprev);
// posprev->prev->next = pos;
// pos->prev = posprev->prev;
//
//}//在pos位置删除
void LTErase(LTNode* pos)
{assert(pos);LTNode* posprev = pos->prev;LTNode* posnext = pos->next;posprev->next = posnext;posnext->prev = posprev;free(pos);
}//销毁
LTNode* LTDestory(LTNode* phead)
{LTNode* cur = phead->next;while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);
}
3.3test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//void test1()
//{
// LTNode* plist = LTInit();
// LTPushBack(plist, 1);
// LTPushBack(plist, 2);
// LTPushBack(plist, 3);
// LTPushBack(plist, 4);
// LTPrint(plist);
// LTPopBack(plist);
// LTPrint(plist);//}
void test2()
{LTNode* plist = LTInit();LTFrontBack(plist, 1);LTFrontBack(plist, 2);LTFrontBack(plist, 3);LTFrontBack(plist, 4);LTPrint(plist);LTErase(3);LTPrint(plist);
}
int main()
{//test1();test2();return 0;
}
相关文章:
数据结构——带头双向循环链表
数据结构——带头双向循环链表 一、带头双向循环链表的定义二、带头双向循环链表的实现2.1初始化创建带头双向循环链表的节点2.2申请新节点2.3节点的初始化2.4带头双向循环链表的尾插2.5带头双向循环链表的头插2.6判空函数2.7带头双向循环链表的打印函数2.8带头双向循环链表的尾…...
MySQL大数据量高速迁移,500GB只需1个小时
在上篇「快、准、稳的实现亿级别MySQL大表迁移」的文章中,介绍了NineData在单张大表场景下的迁移性能和优势。但在大部分场景中,可能遇到的是多张表构成的大数据量场景下的数据搬迁问题。因为搬迁数据量较大,迁移的时长、稳定性及准确性都受到…...
kafka复习:(25)kafka stream
一、java代码: package com.cisdi.dsp.modules.metaAnalysis.rest.kafka2023;import org.apache.kafka.common.serialization.Serdes; import org.apache.kafka.streams.KafkaStreams; import org.apache.kafka.streams.StreamsBuilder; import org.apache.kafka.s…...
接口自动化测试总结
一、什么项目适合做自动化测试? 软件需求变动不频繁 测试脚本的稳定性决定了自动化测试的维护成本。如果软件需求变动过于频繁,测试人员需要根据变动的需求来更新测试用例以及相关的测试脚本,而脚本的维护本身就是一个代码开发的过程&#x…...
【Redis】Lua脚本在Redis中的基本使用及其原子性保证原理
文章目录 背景一、Eval二、EvalSHA三、Redis 对 Lua 脚本的管理3.1 script flush3.2 script exists3.3 script load3.4 script kill 四、Lua在Redis中原子性执行的原理 背景 Lua 本身是一种轻量小巧的脚本语言,在Redis2.6版本开始引入了对Lua脚本的支持。通过在服务…...
汇编--int指令
中断信息可以来自CPU的内部和外部, 当CPU的内部有需要处理的事情发生的时候,将产生需要马上处理的中断信息,引发中断过程。在http://t.csdn.cn/jihpG,我们讲解了中断过程和两种内中断的处理。 这一章中, 我们讲解另一种…...
生成式AI的JavScript技术栈
如果不使用新的软件基础设施技术,就很难理解它们。 至少,a16z 基础设施团队发现了这一点,而且因为我们中的许多人都是以程序员的身份开始职业生涯的,所以我们经常通过实践来学习。 尤其是生成式AI浪潮的情况尤其如此,它…...
从零开始学习软件测试-第39天笔记
接口测试 http消息结构 请求报文 请求行 请求方式 url 协议版本请求头空行请求体响应报文 响应行 协议版本 状态码 状态消息响应头空行响应体 请求参数类型 path参数 写在路径中的 https://xxx.xxx.com/参数值query参数 写在url问号后面,以键值对形式存在 h…...
【多思路附源码】2023高教社杯 国赛数学建模C题思路 - 蔬菜类商品的自动定价与补货决策
赛题介绍 在生鲜商超中,一般蔬菜类商品的保鲜期都比较短,且品相随销售时间的增加而变差, 大部分品种如当日未售出,隔日就无法再售。因此, 商超通常会根据各商品的历史销售和需 求情况每天进行补货。 由于商超销售的蔬…...
Vue2+Vue3基础入门到实战项目(六)——课程学习笔记
镇贴!!! day07 vuex的基本认知 使用场景 某个状态 在 很多个组件 来使用 (个人信息) 多个组件 共同维护 一份数据 (购物车) 构建多组件共享的数据环境 1.创建项目 vue create vuex-demo 2.创建三个组件, 目录如下 |-components |--Son1.…...
QT—基于http协议的网络文件下载
1.常用到的类 QNetworkAccessManager类用于协调网络操作,负责发送网络请求,创建网络响应 QNetworkReply类表示网络请求的响应。在QNetworkAccessManager发送一个网络请求后创建一个网络响应。它提供了以下信号: finished():完成…...
SpringBoot-配置优先级
配置 SpringBoot项目支持的三种格式的配置文件 application.properties:这是最常用的配置文件类型,使用键值对的形式来配置应用程序的属性。可以在该文件中配置应用程序的端口号、数据库连接信息、日志级别等。 application.yml:这是一种更…...
科普初步了解大模型
目录 一、大模型的简单认知 (一)官方定义 (二)聚焦到大语言模型 (三)大模型的应用举例 二、如何得到大模型 (一)整体的一般步骤 训练自己的模型 使用预训练模型 选择适当的…...
Nginx 和 网关的关系是什么
分析&回答 Nginx也可以实现网关,可以实现对api接口的拦截,负载均衡、反向代理、请求过滤等。网关功能可以进行扩展,比如:安全控制,统一异常处理,XXS,SQL注入等;权限控制,黑白名…...
解决springboot项目中的groupId、package或路径的混淆问题
对于像我一样喜欢跳跃着学习的聪明人来说,肯定要学springboot,什么sevlet、maven、java基础,都太老土了,用不到就不学。所以古代的聪明人有句话叫“书到用时方恨少”,测试开源项目时,编译总是报错ÿ…...
Vmware 网络恢复断网和连接
如果你的 虚拟机无法联网了,比如: vmware 无法将网络更改为桥接状态: 没有未桥接的主机网络适配器 等各种稀奇古怪的问题; 按照下面操作 还远默认设置 包你解决各种问题!...
学生来看!如何白嫖内网穿透?点进来!
文章目录 前言本教程解决的问题是:按照本教程方法操作后,达到的效果是前排提醒: 1 搭建虚拟机1.1 下载文件vmvare虚拟机安装包1.2 安装VMware虚拟机:1.3 解压虚拟机文件1.4 虚拟机初始化1.5 没有搜索到解决方式:1.6 虚…...
C++中的stack和queue
文章目录 1. stack的介绍和使用1.1 stack的介绍1.2 stack的使用 2. queue的介绍和使用2.1 queue的介绍2.2 queue的使用 3 priority_queue的介绍和使用3.1 priority_queue的介绍3.2 priority_queue的使用 4. 容器适配器4.1 什么是适配器4.2 STL标准库中stack和queue的底层结构4.…...
Ubuntu-22.04通过RDP协议连接远程桌面
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、RDP是什么?二、配置1.打开远程桌面功能2.验证服务3.防火墙配置4.测试效果 总结 前言 由于一些特殊需要,我需要通过远程桌面连接到U…...
20230908java面经整理
1.cpp和java的区别 cpp可以多重继承,对表java中的实现多个接口 cpp支持运算符重载、goto、默认函数参数 cpp自动强转,导致不安全;java必须显式强转 java提供垃圾回收机制,自动管理内存分配,当gc要释放无用对象时调用f…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
云原生玩法三问:构建自定义开发环境
云原生玩法三问:构建自定义开发环境 引言 临时运维一个古董项目,无文档,无环境,无交接人,俗称三无。 运行设备的环境老,本地环境版本高,ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...
文件上传漏洞防御全攻略
要全面防范文件上传漏洞,需构建多层防御体系,结合技术验证、存储隔离与权限控制: 🔒 一、基础防护层 前端校验(仅辅助) 通过JavaScript限制文件后缀名(白名单)和大小,提…...
汇编语言学习(三)——DoxBox中debug的使用
目录 一、安装DoxBox,并下载汇编工具(MASM文件) 二、debug是什么 三、debug中的命令 一、安装DoxBox,并下载汇编工具(MASM文件) 链接: https://pan.baidu.com/s/1IbyJj-JIkl_oMOJmkKiaGQ?pw…...
