带头+双向+循环链表
前言:
前面我们已经学习了单链表的结构及其功能特点,也了解了单链表在实现一些功能时出现的一些缺点,比如在删除某个节点前面一个节点时就需要再开一个变量来存放前面一个节点的信息,这样就显得不灵活,为了使链表实现功能更加灵活,我给来介绍带头双向循环链表,这种链表可以看成是单链表的升级版。
什么是带头双向循环链表?
带头双向循环链表是一种常见的链表数据结构,它是由若干个结点组成的链式数据结构,每个结点包含两个指针,分别指向前一个结点和后一个结点。与普通的单向链表只能单向遍历不同,在双向循环链表中,可以使用前后两个方向进行遍历。
带头双向循环链表的优点?
带头链表意味着在链表头部添加一个空的头结点,这个头结点不包含数据,仅包含指向链表首尾结点的指针,它主要作用是简化链表的操作。在插入、删除、查找等操作时,可以直接从头结点开始操作,无需特殊处理首尾结点。
双向循环意味着链表的每一个节点都可以随意的找到上一个节点以及下一个节点,并且头节点的前驱指针指向尾节点,尾节点的后驱指针指向头节点,这样整个链表就形成了一个环状结构,可以任意方向遍历整个链表。
由于带头双向循环链表具有链表的优点,同时又兼具循环队列的特点,因此在实际应用中被广泛使用。比如在实现 LRU 缓存淘汰算法、模拟双向队列等场景中都有使用。
代码实现
DList.h头文件
各种功能函数的声明以及数据的定义:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType _data;struct ListNode* _next;struct ListNode* _prev;
}ListNode;// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* phead);
// 双向链表打印
void ListPrint(ListNode* phead);
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* phead);
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* phead);
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
DList.c文件
各种功能函数的具体实现细节:
#include"DList.h"// 创建返回链表的头结点.
ListNode* ListCreate() {ListNode* NewList = (ListNode*) malloc(sizeof(ListNode));if (NewList == NULL) {perror("malloc: fail");}NewList->_data = -1;NewList->_prev = NewList;NewList->_next = NewList;return NewList;
}//创造节点
static ListNode* CreatNode(LTDataType x) {ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL) {perror("malloc:fail");}newnode->_data = x;newnode->_next = newnode;newnode->_prev = newnode;return newnode;
}// 双向链表销毁
void ListDestory(ListNode* phead) {assert(phead);ListNode* cur = phead;cur = cur->_next;while (cur!=phead) {ListNode* temp = cur->_next;cur->_prev->_next = cur->_next;cur->_next->_prev = cur->_prev;free(cur);cur = temp;}free(cur);cur = NULL;
}// 双向链表打印
void ListPrint(ListNode* phead) {assert(phead);ListNode* cur = phead->_next;printf("%d<=>", phead->_data);while (cur!= phead) {printf(" %d <=>", cur->_data);cur = cur->_next;}printf("%d\n", phead->_data);
}// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x) {assert(phead);ListNode* newnode = CreatNode(x);ListNode* tail = phead->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = phead;phead->_prev = newnode;
}
// 双向链表尾删
void ListPopBack(ListNode* phead) {assert(phead&&(phead->_next!=phead));ListNode* tail = phead->_prev;phead->_prev = tail->_prev;tail->_prev->_next = phead;free(tail);tail = NULL;
}
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x) {assert(phead);ListNode* newnode = CreatNode(x);phead->_next->_prev = newnode;newnode->_next = phead->_next;newnode->_prev = phead;phead->_next = newnode;
}// 双向链表头删
void ListPopFront(ListNode* phead) {assert(phead&&(phead->_next!=phead));ListNode* head = phead->_next;phead->_next->_next->_prev = phead;phead->_next = phead->_next->_next;free(head);head = NULL;
}// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x) {assert(phead && (phead->_next != phead));//ListNode* newnode = CreatNode(x);ListNode* cur = phead->_next;while (cur != phead) {if (cur->_data == x) {return cur;}cur = cur->_next;}return NULL;
}// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x) {assert(pos);ListNode* newnode = CreatNode(x);pos->_prev->_next = newnode;newnode->_prev = pos->_prev;newnode->_next = pos;pos->_prev = newnode;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos) {assert(pos&&(pos->_next!=pos));ListNode* temp = pos;pos->_prev->_next = pos->_next;pos->_next->_prev = pos->_prev;free(temp);temp = NULL;
}
test.c测试文件
分块测试各个部分的功能
#include"DList.h"void test1()//测试尾插、尾删
{// 创建返回链表的头结点.ListNode* phead=NULL;phead= ListCreate();ListPushBack(phead, 1);//尾插ListPushBack(phead, 2);ListPushBack(phead, 3);ListPrint(phead);printf("尾删\n");ListPopBack(phead);//尾删ListPrint(phead);printf("尾删\n");ListPopBack(phead);//尾删ListPrint(phead);//ListPopBack(phead);//ListPrint(phead);ListDestory(phead);//销毁链表
}void test2()//测试头插、头删
{// 创建返回链表的头结点.ListNode* phead = NULL;phead = ListCreate();ListPushFront(phead, 1);//头插,头删ListPushFront(phead, 2);ListPushFront(phead, 3);ListPrint(phead);printf("头删\n");ListPopFront(phead);ListPrint(phead);printf("头删\n");ListPopFront(phead);//ListPopFront(phead);//ListPopFront(phead);//ListPopFront(phead);ListPrint(phead);ListDestory(phead);}
void test3()//测试查找,随机位置插入,随机位置删除
{// 创建返回链表的头结点.ListNode* phead = NULL;phead = ListCreate();//初始化链表数据 1 2 3 4 5ListPushFront(phead, 1);ListPushFront(phead, 2);ListPushFront(phead, 3);ListPushFront(phead, 4);ListPushFront(phead, 5);ListPrint(phead);//ListNode* targetnode = ListFind(phead, 5);ListNode* targetnode = ListFind(phead, 4);if (targetnode == NULL) {//没有找到printf("没有找到\n");}else {printf("找到%d了\n",targetnode->_data);/*ListErase(targetnode);printf("删除这个节点\n");*/ListInsert(targetnode, 10);//在目标节点前面插入printf("在这个节点插入一个数\n");ListPrint(phead);}ListNode* targetnode2 = ListFind(phead, 4);if (targetnode2 == NULL) {//没有找到printf("没有找到\n");}else {printf("找到%d了\n", targetnode2->_data);ListErase(targetnode);printf("删除这个节点\n");ListPrint(phead);}//ListPopFront(phead);//ListPrint(phead);ListDestory(phead);phead = NULL;
}int main() {test1();//test2();//test3();return 0;
}
test1运行结果
test2运行结果
test3运行结果
相关文章:

带头+双向+循环链表
前言: 前面我们已经学习了单链表的结构及其功能特点,也了解了单链表在实现一些功能时出现的一些缺点,比如在删除某个节点前面一个节点时就需要再开一个变量来存放前面一个节点的信息,这样就显得不灵活,为了使链表实现功…...

Leetcode_2:两数相加
题目描述: 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外ÿ…...

Pytorch实战教程(一)-神经网络与模型训练
0. 前言 人工神经网络 (Artificial Neural Network, ANN) 是一种监督学习算法,其灵感来自人类大脑的运作方式。类似于人脑中神经元连接和激活的方式,神经网络接受输入,通过某些函数在网络中进行传递,导致某些后续神经元被激活,从而产生输出。函数越复杂,网络对于输入的数…...

【MySQL】手把手教你centos7下载MySQL
centos7下载MySQL 前言正式开始卸载不需要的环境(如果你之前没有安装过数据库相关的东西可以跳过)下载mysql登录mysql登陆⽅法⼀【不⾏就下⼀个】登陆⽅法⼆【不⾏就下⼀个】登录方式三 前言 安装和卸载MySQL都用系统的root权限,更方便一点&…...
openlayers
OpenLayers使用_openlayers中文官网-CSDN博客...

力扣每日一道系列 --- LeetCode 88. 合并两个有序数组
📷 江池俊: 个人主页 🔥个人专栏: ✅数据结构探索 ✅LeetCode每日一道 🌅 有航道的人,再渺小也不会迷途。 文章目录 思路1:暴力求解思路2:原地合并 LeetCode 88. 合并两个有序数组…...

Android Studio(项目收获)
取消按钮默认背景色 像按钮默认背景色为深蓝色,即使使用了background属性指定颜色也不能生效。 参考如下的解决方法: 修改/res/values/themes.xml中的指定内容如下: <style name"Theme.TianziBarbecue" parent"Theme.Mater…...
MQ写满的情况如何处理?
**MQ(Message Queue)**写满的情况通常指消息队列中的存储空间已经被用尽,无法再接收新的消息。处理MQ写满的情况涉及到多个方面,包括监控、调整配置、增加资源、以及处理积压消息等。下面是一些处理MQ写满的 常见方法:…...
点名(缺失的数字),剑指offer,力扣
目录 我们直接看题解吧: 审题目事例提示: 方法: 解题思路(二分法): 代码: 方法二:直接遍历 题目地址 LCR 173. 点名 - 力扣(LeetCode) 今天刷点名(…...

云安全—Dashboard 攻击面
0x00 前言 众所周知,如果只是一味的REST接口或者命令行话的操作方式,就会变相的提高操作门款,并且不会有很好的呈现方式,所以就有了web ui的方式,也就是Dashboar面板,本篇主要讨论一下关于Dashboar面板的概…...

FCOS难点记录
FCOS 中有计算 特征图(Feature map中的每个特征点到gt_box的左、上、右、下的距离) 1、特征点到gt_box框的 左、上、右、下距离计算 x coords[:, 0] # h*w,2 即 第一列y coords[:, 1] l_off x[None, :, None] - gt_boxes[..., 0][:, No…...

java通过FTP跨服务器动态监听读取指定目录下文件数据
背景: 1、文件数据在A服务器(windows)(不定期在指定目录下生成),项目应用部署在B服务器(Linux); 2、项目应用在B服务器,监听A服务器指定目录,有新…...

5G边缘计算网关的功能及作用
5G边缘计算网关具有多种功能。 首先,它支持智能云端控制,可以通过5G/4G/WIFI等无线网络将采集的数据直接上云,实现异地远程监测控制、预警通知、报告推送和设备连接等工作。 其次,5G边缘计算网关可以采集各种数据,包…...

阿里云AIGC小说生成【必得京东卡】
任务步骤 此文真实可靠不做虚假宣传,绝对真实,可截图为证。 领取任务 链接(复制到wx打开):#小程序://ITKOL/1jw4TX4ZEhykWJd 教程实践 打开函数计算控制台 应用->创建应用->人工智能->通义千问 AI 助手-…...

数据结构之AVL树
map/multimap/set/multiset这几个容器有个共同点是: 其底层都是按照二叉搜索树来实现的,但是普通的二叉搜索树有其自身的缺陷, 假如往树中插入的元素有序或者接近有序, 二叉搜索树就会退化成单支树, 时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了…...

如何用Java实现一个基于机器学习的情感分析系统,用于分析文本中的情感倾向
背景:练习两年半(其实是两周半),利用工作闲余时间入门一下机器学习,本文没有完整的可实施的案例,由于知识体系不全面,目前代码只能运行,不能准确的预测 卡点: 1 由于过…...
开发聚合支付的的意义
开发聚合支付的意义在于整合各种支付方式,为消费者和商家提供便捷高效的支付体验,同时满足商家的多元化支付需求,提高支付效率和用户体验。 具体来说,聚合支付具有以下意义: 方便快捷:聚合支付整合了多种…...

ChatGPT生产力|中科院学术ChatGPT优化配置
资源链接:GitHub - binary-husky/gpt_academic b站配置讲解链接:chatgpt-academic 新手运行官方精简指南(科研chatgpt拓展) 某知配置图文讲解:图文详解:在windows中部署ChatGPT学术版 - 知乎 (zhihu.com) 一…...
语音播报speechSynthesis最简单的例子(亲测有用)
最简单的例子,在chrome上亲测有效: const utterThis new SpeechSynthesisUtterance(我来试试呀); const synth window.speechSynthesis; synth.speak(utterThis);加入配置,可以配置语言、音量、语速、音高,继续玩: …...
呆头鹅-全自动视频混剪,批量剪辑批量剪视频,探店带货系统,精细化顺序混剪,故事影视解说,视频处理大全,精细化顺序混剪,多场景裂变,多视频混剪
视频闪闪-全自动视频混剪,探店带货系统,多视频混剪,让你成为视频处理大师! 一、全自动视频混剪 www.shipinshanshan.com 你是否曾经厌烦于冗长的视频剪辑过程?是否曾经为了一个短短的混剪视频而熬夜加班?现…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...