当前位置: 首页 > news >正文

数据结构——带头双向循环链表

数据结构——带头双向循环链表

  • 一、带头双向循环链表的定义
  • 二、带头双向循环链表的实现
    • 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大表迁移」的文章中&#xff0c;介绍了NineData在单张大表场景下的迁移性能和优势。但在大部分场景中&#xff0c;可能遇到的是多张表构成的大数据量场景下的数据搬迁问题。因为搬迁数据量较大&#xff0c;迁移的时长、稳定性及准确性都受到…...

kafka复习:(25)kafka stream

一、java代码&#xff1a; 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…...

接口自动化测试总结

一、什么项目适合做自动化测试&#xff1f; 软件需求变动不频繁 测试脚本的稳定性决定了自动化测试的维护成本。如果软件需求变动过于频繁&#xff0c;测试人员需要根据变动的需求来更新测试用例以及相关的测试脚本&#xff0c;而脚本的维护本身就是一个代码开发的过程&#x…...

【Redis】Lua脚本在Redis中的基本使用及其原子性保证原理

文章目录 背景一、Eval二、EvalSHA三、Redis 对 Lua 脚本的管理3.1 script flush3.2 script exists3.3 script load3.4 script kill 四、Lua在Redis中原子性执行的原理 背景 Lua 本身是一种轻量小巧的脚本语言&#xff0c;在Redis2.6版本开始引入了对Lua脚本的支持。通过在服务…...

汇编--int指令

中断信息可以来自CPU的内部和外部&#xff0c; 当CPU的内部有需要处理的事情发生的时候&#xff0c;将产生需要马上处理的中断信息&#xff0c;引发中断过程。在http://t.csdn.cn/jihpG&#xff0c;我们讲解了中断过程和两种内中断的处理。 这一章中&#xff0c; 我们讲解另一种…...

生成式AI的JavScript技术栈

如果不使用新的软件基础设施技术&#xff0c;就很难理解它们。 至少&#xff0c;a16z 基础设施团队发现了这一点&#xff0c;而且因为我们中的许多人都是以程序员的身份开始职业生涯的&#xff0c;所以我们经常通过实践来学习。 尤其是生成式AI浪潮的情况尤其如此&#xff0c;它…...

从零开始学习软件测试-第39天笔记

接口测试 http消息结构 请求报文 请求行 请求方式 url 协议版本请求头空行请求体响应报文 响应行 协议版本 状态码 状态消息响应头空行响应体 请求参数类型 path参数 写在路径中的 https://xxx.xxx.com/参数值query参数 写在url问号后面&#xff0c;以键值对形式存在 h…...

【多思路附源码】2023高教社杯 国赛数学建模C题思路 - 蔬菜类商品的自动定价与补货决策

赛题介绍 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c; 商超通常会根据各商品的历史销售和需 求情况每天进行补货。 由于商超销售的蔬…...

Vue2+Vue3基础入门到实战项目(六)——课程学习笔记

镇贴&#xff01;&#xff01;&#xff01; day07 vuex的基本认知 使用场景 某个状态 在 很多个组件 来使用 (个人信息) 多个组件 共同维护 一份数据 (购物车) 构建多组件共享的数据环境 1.创建项目 vue create vuex-demo 2.创建三个组件, 目录如下 |-components |--Son1.…...

QT—基于http协议的网络文件下载

1.常用到的类 QNetworkAccessManager类用于协调网络操作&#xff0c;负责发送网络请求&#xff0c;创建网络响应 QNetworkReply类表示网络请求的响应。在QNetworkAccessManager发送一个网络请求后创建一个网络响应。它提供了以下信号&#xff1a; finished()&#xff1a;完成…...

SpringBoot-配置优先级

配置 SpringBoot项目支持的三种格式的配置文件 application.properties&#xff1a;这是最常用的配置文件类型&#xff0c;使用键值对的形式来配置应用程序的属性。可以在该文件中配置应用程序的端口号、数据库连接信息、日志级别等。 application.yml&#xff1a;这是一种更…...

科普初步了解大模型

目录 一、大模型的简单认知 &#xff08;一&#xff09;官方定义 &#xff08;二&#xff09;聚焦到大语言模型 &#xff08;三&#xff09;大模型的应用举例 二、如何得到大模型 &#xff08;一&#xff09;整体的一般步骤 训练自己的模型 使用预训练模型 选择适当的…...

Nginx 和 网关的关系是什么

分析&回答 Nginx也可以实现网关&#xff0c;可以实现对api接口的拦截&#xff0c;负载均衡、反向代理、请求过滤等。网关功能可以进行扩展&#xff0c;比如&#xff1a;安全控制&#xff0c;统一异常处理&#xff0c;XXS,SQL注入等&#xff1b;权限控制&#xff0c;黑白名…...

解决springboot项目中的groupId、package或路径的混淆问题

对于像我一样喜欢跳跃着学习的聪明人来说&#xff0c;肯定要学springboot&#xff0c;什么sevlet、maven、java基础&#xff0c;都太老土了&#xff0c;用不到就不学。所以古代的聪明人有句话叫“书到用时方恨少”&#xff0c;测试开源项目时&#xff0c;编译总是报错&#xff…...

Vmware 网络恢复断网和连接

如果你的 虚拟机无法联网了&#xff0c;比如&#xff1a; vmware 无法将网络更改为桥接状态: 没有未桥接的主机网络适配器 等各种稀奇古怪的问题&#xff1b; 按照下面操作 还远默认设置 包你解决各种问题&#xff01;...

学生来看!如何白嫖内网穿透?点进来!

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1 搭建虚拟机1.1 下载文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压虚拟机文件1.4 虚拟机初始化1.5 没有搜索到解决方式&#xff1a;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协议连接远程桌面

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、RDP是什么&#xff1f;二、配置1.打开远程桌面功能2.验证服务3.防火墙配置4.测试效果 总结 前言 由于一些特殊需要&#xff0c;我需要通过远程桌面连接到U…...

20230908java面经整理

1.cpp和java的区别 cpp可以多重继承&#xff0c;对表java中的实现多个接口 cpp支持运算符重载、goto、默认函数参数 cpp自动强转&#xff0c;导致不安全&#xff1b;java必须显式强转 java提供垃圾回收机制&#xff0c;自动管理内存分配&#xff0c;当gc要释放无用对象时调用f…...

uniapp 开发App 网络异常如何处理

我对该问题思考的不是很清楚&#xff0c;目前只想到了基本的解决方案 第一、客户端的网络异常&#xff08;断网&#xff09; 1. 断网情况 一定要弹出信息提示&#xff0c;目前最好的解决方式就是在uni.request封装的统一方法中写提示 //1. 封装的网络请求 async function se…...

docker安装常用软件

Linux系统安装docker请参考&#xff1a;https://mp.csdn.net/mp_blog/creation/editor/128176825 docker安装mysql 1、拉镜像&#xff1a;docker pull mysql:8.0.26 2、创建数据目录&#xff1a; mkdir -p /mnt/data/mysql/data mkdir -p /mnt/data/mysql/logs mkdir -p /mn…...

CocosCreator3.8研究笔记(五)CocosCreator 脚本说明及使用(下)

在Cocos Creator中&#xff0c;脚本代码文件分为模块和插件两种方式&#xff1a; 模块一般就是项目的脚本&#xff0c;包含项目中创建的代码、引擎模块、第三方模块。 插件脚本&#xff0c;是指从 Cocos Creator 属性检查器中导入的插件&#xff0c;一般是引入第三方引入库文件…...

Adobe Acrobat Reader界面改版 - 解决方案

问题 日期&#xff1a;2023年9月 Adobe Acrobat Reader下文简称Adobe PDF Reader&#xff0c;此软件会自动进行更新&#xff0c;当版本更新至2023.003.20284版本后。 软件UI界面会大改版&#xff1a;书签页变成了右边、工具栏变到了左边、缩放按钮变到了右下角&#xff0c;如…...

实用调试技巧(2)

文章目录 6. 如何写出好&#xff08;易于调试&#xff09;的代码6.1 优秀的代码&#xff1a;6.2 示范&#xff1a;6.3 const的作用 7. 编程常见的错误7.1 编译型错误7.2 链接型错误7.3 运行时错误 附&#xff1a; 6. 如何写出好&#xff08;易于调试&#xff09;的代码 6.1 优…...

海外ASO优化之如何优化游戏应用

如果我们发布了一款手机游戏或者管理了一款手机游戏&#xff0c;那么需要确保我们的手机游戏对合适的人可见&#xff0c;目的是增加应用的下载量。 1、优化游戏元数据的关键词。 Apple和Google在应用商店中为我们提供有限的空间&#xff0c;来描述手机游戏及其优势。我们需要使…...

SpringMVC: Java Web应用开发的框架之选

引言 在当今的软件开发领域中&#xff0c;Web应用的需求不断增长。为了满足这种需求&#xff0c;各种Web框架应运而生。其中&#xff0c;SpringMVC作为一种优秀的Java Web框架&#xff0c;受到广泛关注和使用。本文将以文章的形式给您讲解SpringMVC的重要概念、工作原理和核心…...

【华为设备升级】AR路由器升级设备软件示例

升级设备软件示例 通过介绍设备升级的具体步骤&#xff0c;帮助用户顺利完成系统设备升级。 组网需求 设备当前系统软件版本已经不能满足用户需要&#xff0c;用户需要更大的规格和部署更多的特性&#xff0c;此时用户需要对系统软件进行升级。 如图1所示&#xff0c;网络中的某…...

Dataset 的一些 Java api 操作

文章目录 一、使用 Java API 和 JavaRDD<Row> 在 Spark SQL 中向数据帧添加新列二、foreachPartition 遍历 Dataset三、Dataset 自定义 Partitioner四、Dataset 重分区并且获取分区数 一、使用 Java API 和 JavaRDD 在 Spark SQL 中向数据帧添加新列 在应用 mapPartition…...

Vue + Element UI 前端篇(十一):第三方图标库

Vue Element UI 实现权限管理系统 前端篇&#xff08;十一&#xff09;&#xff1a;第三方图标库 使用第三方图标库 用过Elment的同鞋都知道&#xff0c;Element UI提供的字体图符少之又少&#xff0c;实在是不够用啊&#xff0c;幸好现在有不少丰富的第三方图标库可用&…...