双向带头循环链表(图解)

文章目录

  • 头节点(哨兵位)
  • 双向循环结构
    • 头插
    • 尾插
    • 头删
    • 尾删
    • 在指定位置之前插入数据
    • 删除指定位置之前的数据
    • 销毁链表
  • 全部代码
  • 结语

单链表地址

头节点(哨兵位)

什么是头节点呢?头节点也叫哨兵节点,他在链表中进行不了任何操作,只是用来放哨用的,在单链表中我们当我们尾插的时候我们呢需要判断此时的节点是否是空,如果不是空我们才可以尾插,是空的话我们直接赋值,而有了头节点我们就不需要判断是否为空了,直接在头节点后插入即可。这就是头节点的好处,尾插,尾删等也有好处(下文再说)

双向循环结构

单链表中我们只有一个指针指向下一个节点,而双向链表则需要两个指针,一个指向前一个节点,一个指向后一个节点,由于我们需要循环起来,所以最后一个节点我们要指向头节点(这里是指第一个节点) 为了避免搞混我下文把第一个节点称为头节点,哨兵节点就为哨兵节点

哨兵节点如下:
在这里插入图片描述
注意这里我们是要创建一个哨兵节点,所以我们需要返回一个节点,而我们接下来的操作都是在哨兵节点基础上进行的,所以每一次进行基本增删操作的时候可以传一级指针,因为我们接受的哨兵节点肯定是一个指针类型的。

结构定义如下:
在这里插入图片描述

代码实现:

//创建哨兵节点
List* ListHeadCreat()
{List* newhead = (List*)malloc(sizeof(List));if (newhead == NULL){printf("malloc fail!\n");exit(-1);}newhead->val = -1;newhead->next = newhead->prev = newhead;return newhead;
}

头插

首先我们要创建一个新的节点,先让这个新的节点指向自己
头插这里分两种情况:
1. 只有一个哨兵节点
(1)
newNode (新节点)的next指向phead,再让newNodeprev指向phead
(2)
phead->next指向newNode,再让phead->prev指向newNode
2. 不止一个节点
(1)
newNode的next指向phead->prev(此时由于链表的最后一个元素就是哨兵节点上一个节点),再让newNode->prevphead
(2)
phead->prev指向newNode,phead->prev->next(最后一个节点的下一个节点)指向newNode
在这里插入图片描述

代码实现:

//头插
void ListPushFront(List* phead, LDataType x)
{assert(phead);List* newNode = BuyNode(x);	newNode->prev = phead;newNode->next = phead->next;phead->next = newNode;//如果只有一个节点,要把此时的哨兵节点的上一个节点指向新插入的节点if (phead->prev == phead)phead->prev = newNode;
}

在这里插入图片描述

尾插

尾插无论是只有哨兵节点还是多个节点,代码操作都是一样的。
(1)
newNode->next指向phead
newNode->prev
指向最后一个节点
(2)
phead->prev->next(最后一个节点)指向newNode
phead->prev(哨兵节点指向尾)指向newNode

在这里插入图片描述
代码实现:

//尾插
void ListPushBack(List* phead, LDataType x)
{assert(phead);List* newNode = BuyNode(x);newNode->next = phead;newNode->prev = phead->prev;phead->prev->next = newNode;phead->prev = newNode;
}

在这里插入图片描述

头删

头删我们得先保证链表中还有元素,所以我们需要判断以下phead->next是否还是Phead,如果是就说明此时只有哨兵节点

头删也是两种情况都是一样的代码
这里把第一个节点写为del,我们只需要让phead->next指向del,del->prev指向phead,这样就相当于把del从这个链表中断开了,接着就放心的free掉就可以了。

在这里插入图片描述
代码实现:

//头删
void ListPopFront(List* phead)
{assert(phead && phead->next != phead);List* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}

在这里插入图片描述

尾删

尾删跟头删差不多,只不过我们要找到最后一个节点(phead->prev),这就是双链表的好处

尾删的两种情况也都是一致的:

End = phead->prev(最后一个节点)
End->prev->next = phead
phead->next = End->prev
free(End)

在这里插入图片描述
代码实现:

//尾删
void ListPopBack(List* phead)
{assert(phead && phead->prev != phead);List* End = phead->prev;End->prev->next = phead;phead->prev = End->prev;free(End);End = NULL;
}

在这里插入图片描述

在指定位置之前插入数据

如下图:
无论在什么位置之前插入元素,都是一样的代码,首先我们还得写一个查找函数,这里只需要遍历找值就可以了,所以不单独写出来了,跟这里写一块了。
在这里插入图片描述
代码实现:

//查找指定元素
List* ListFind(List* phead, LDataType x)
{assert(phead && phead->next != phead);List* cur = phead->next;while (cur != phead){if (cur->val == x)return cur;cur = cur->next;}return NULL;
}//在指定位置之前插入元素
void ListInsertFront(List* pos, LDataType x)
{assert(pos);List* newNode = BuyNode(x);newNode->prev = pos->prev;newNode->next = pos;pos->prev->next = newNode;pos->prev = newNode;
}

在这里插入图片描述

删除指定位置之前的数据

这里需要注意,由于我们删除的是指定位置之前的数据,所以我们要保证这个pos->prev这个位置不能是哨兵节点,同时pos->prev->prev不能为空,必须有至少两个节点才能删除指定位置之前的数据

在这里插入图片描述
代码实现:

//删除指定位置之前的元素
void ListEarseFront(List* pos, List* phead)
{assert(pos && pos->prev && pos->prev != phead && pos->prev->prev);List* del = pos->prev;pos->prev->prev->next = pos;pos->prev = pos->prev->prev;free(del);del = NULL;
}

在这里插入图片描述

销毁链表

销毁链表分为两种方式:

(1)
由于我们是传入的phead是一级指针,而我们函数的参数也是用的一级指针接受的,因为函数的形参只是实参的一份临时拷贝,由于每次malloc的内存都是在区内开辟的所以我们可以做到释放每一个节点,但是phead这个指针却并不能置为空,如果不置空的话,他指向了被释放掉了的空间,还能找到那个空间,所以此时phead就是野指针,我们也只能在调用**ListDesTory()**这个销毁函数的下面手动把phead置为空
代码实现:

void ListDesTory(List* phead)
{assert(phead);List* cur = phead->next;while (cur != phead){List* next = cur->next;free(cur);cur = next;}free(phead);
}

手动置空
在这里插入图片描述

(2)
当我们想要在函数内部修改变量的时候,我们因该传入它的地址,就像如果我们想要在函数里面修改 int a = 1这个a的值,那我们应该传入的是**(&a),同时我们呢要用int*** 的指针来接收,同样的这里我们呢传入**(&phead),那我们就要用(List**)**来接受,这样在函数内部就可以把phead置为空了

代码实现:

void ListDesTory(List** pphead)
{assert(pphead && *pphead);List* cur = (*pphead)->next;while (cur != (*pphead)){List* next = cur->next;free(cur);cur = next;}free((*pphead));*pphead = NULL;	
}

全部代码

List.h

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int LDataType;
typedef struct List
{struct List* prev;struct List* next;LDataType val;
}List;//创建哨兵节点
List* ListHeadCreat();
//打印节点
void ListPrint(List* phead);
//头插
void ListPushFront(List* phead,LDataType x);
//尾插
void ListPushBack(List* phead, LDataType x);//头删
void ListPopFront(List* phead);
//尾删
void ListPopBack(List* phead);//查找指定元素
List* ListFind(List* phead, LDataType x);
//在指定位置之前插入元素
void ListInsertFront(List* pos,LDataType x);
//删除指定位置之前的元素
void ListEarseFront(List* pos, List* phead);
//销毁链表
void ListDesTory(List* phead);
//销毁链表
//void ListDesTory(List** pphead);

List.c

#define _CRT_SECURE_NO_WARNINGS#include "Lish.h"//创建哨兵节点
List* ListHeadCreat()
{List* newhead = (List*)malloc(sizeof(List));if (newhead == NULL){printf("malloc fail!\n");exit(-1);}newhead->val = -1;newhead->next = newhead->prev = newhead;return newhead;
}//打印节点
void ListPrint(List* phead)
{assert(phead && phead->next);List* cur = phead->next;while (cur != phead){printf("%d ", cur->val);cur = cur->next;}
}List* BuyNode(LDataType x)
{List* newNode = (List*)malloc(sizeof(List));if (newNode == NULL){printf("malloc fail!\n");exit(-1);}newNode->val = x;newNode->next = newNode->prev = newNode;return newNode;
}
//头插
void ListPushFront(List* phead, LDataType x)
{assert(phead);List* newNode = BuyNode(x);	newNode->prev = phead;newNode->next = phead->next;phead->next = newNode;//如果只有一个节点,要把此时的哨兵节点的上一个节点指向新插入的节点if (phead->prev == phead)phead->prev = newNode;
}
//尾插
void ListPushBack(List* phead, LDataType x)
{assert(phead);List* newNode = BuyNode(x);newNode->next = phead;newNode->prev = phead->prev;phead->prev->next = newNode;phead->prev = newNode;
}//头删
void ListPopFront(List* phead)
{assert(phead && phead->next != phead);List* del = phead->next;del->next->prev = phead;phead->next = del->next;free(del);del = NULL;
}
//尾删
void ListPopBack(List* phead)
{assert(phead && phead->prev != phead);List* End = phead->prev;End->prev->next = phead;phead->prev = End->prev;free(End);End = NULL;
}//查找指定元素
List* ListFind(List* phead, LDataType x)
{assert(phead && phead->next != phead);List* cur = phead->next;while (cur != phead){if (cur->val == x)return cur;cur = cur->next;}return NULL;
}//在指定位置之前插入元素
void ListInsertFront(List* pos, LDataType x)
{assert(pos);List* newNode = BuyNode(x);newNode->prev = pos->prev;newNode->next = pos;pos->prev->next = newNode;pos->prev = newNode;
}//删除指定位置之前的元素
void ListEarseFront(List* pos, List* phead)
{assert(pos && pos->prev && pos->prev != phead && pos->prev->prev);List* del = pos->prev;pos->prev->prev->next = pos;pos->prev = pos->prev->prev;free(del);del = NULL;
}
//销毁链表
void ListDesTory(List* phead)
{assert(phead);List* cur = phead->next;while (cur != phead){List* next = cur->next;free(cur);cur = next;}free(phead);
}
销毁链表
//void ListDesTory(List** pphead)
//{
//	assert(pphead && *pphead);
//	List* cur = (*pphead)->next;
//	while (cur != (*pphead))
//	{
//		List* next = cur->next;
//		free(cur);
//		cur = next;
//	}
//	free((*pphead));
//	*pphead = NULL;	
//}

结语

OK了,链表在单链表和双向循环带头链表写完了就告一段落了,感觉有帮助的话可以点点赞,如果有哪写错了欢迎指出来~
在这里插入图片描述

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

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

相关文章

使用Flask构建POST请求的Web应用

文章目录 准备工作创建路由处理POST请求创建表单页面运行应用结论 在Web开发中&#xff0c;处理POST请求是一项常见任务&#xff0c;特别是在构建表单提交、用户注册和数据提交等功能时。Flask是一个简单而强大的Python Web框架&#xff0c;它提供了方便的工具来处理HTTP请求&a…

文章解读与仿真程序复现思路——电力自动化设备EI\CSCD\北大核心《考虑微电网联盟协调运行的用户侧共享储能多计费方式博弈定价方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

打开远程连接的命令是什么?

远程连接是一种能够在不同设备之间建立连接并共享信息的技术。在许多情况下&#xff0c;我们需要通过远程连接来访问其他设备或处理一些远程任务。本文将介绍一些常用的打开远程连接的命令。 使用SSH连接远程设备 SSH&#xff08;Secure Shell&#xff09;是一种安全的网络协议…

Matlab/simulink永磁直驱风机的建模仿真

Matlab/simulink直驱永磁同步风机的建模仿真&#xff0c;跟随风速波动效果好&#xff0c;可以作为后期科研的基础模型

达索电磁仿真软件CST查看方式和消除2D/3D图的反射效果【官方教程】

消除2D/3D图的反射效果 查看和分析2D/3D结果时消除不必要的反射效果 View > Options > View Options > Specials 正面看CST软件的3D场分布图时&#xff0c;如下图所示&#xff0c;因为反射效果导致无法看清楚。本章节介绍一下相应的解决方法。 因为View Options的S…

RabbitMQ的用途

RabbitMQ主要有四个用途&#xff0c;分别是应用解耦、异步提速、削峰填谷、消息分发。详情讲解如下&#xff1a; RabbitMQ介绍、解耦、提速、削峰、分发 详解、RabbitMQ安装 可视化界面讲解 1.应用解耦&#xff1a;提高系统容错性和可维护性 2.异步提速&#xff1a;提升用户体验…

【18-Ⅰ】Head First Java 学习笔记

HeadFirst Java 本人有C语言基础&#xff0c;通过阅读Java廖雪峰网站&#xff0c;简单速成了java&#xff0c;但对其中一些入门概念有所疏漏&#xff0c;阅读本书以弥补。 第一章 Java入门 第二章 面向对象 第三章 变量 第四章 方法操作实例变量 第五章 程序实战 第六章 Java…

PyCharm粘贴失灵?一文教你快速恢复!(如何解决Pycharm无法粘贴的问题)

文章目录 💢 问题 💢🏡 演示环境 🏡💯 解决方案 💯⚓️ 相关链接 ⚓️💢 问题 💢 "为什么你的代码编辑器突然变得不听话了?"最近在使用pycharm的时候遇到了一个问题,就是在pycharm中无法使用粘贴功能,后面经过一番折腾得到了解决,现在将我的解决…

scanf留下的那一片云彩

【题目描述】 给出一个由O和X组成的串&#xff08;长度为1&#xff5e;80&#xff09;&#xff0c;统计得分。每个O的得分为目前连续出现的O的个数&#xff0c;X的得分为0。例如&#xff0c;OOXXOXXOOO的得分为1200100123。 输入第一行表示有n个字符串&#xff0c;后续是n行字…

自然资源-城镇开发边界内详细规划编制技术指南解读

自然资源-城镇开发边界内详细规划编制技术指南解读

Xilinx 千兆以太网TEMAC IP核 AXI4-Lite接口信号

在AX4总线标准中&#xff0c;AXI4-Lite主要由向她址映射型通信。TEMAC的管理法口采用AXI4-Lite标准接口&#xff0c;TEMAC核的AX14-Lite接口信号如表1所示&#xff0c;根据AX14-Lite标准&#xff0c;接口角色分为主接口(Maser Interface)和从接口(Slave Interface)。主接口为通…

收放卷伺服控制系统详细算法介绍(电子齿轮+张力PID卷绕轴控制功能块)

收放卷控制系统涉及的内容非常多,这里我们介绍全伺服系统利用电子齿轮指令实现主从轴的比例随动速度控制,收放卷控制算法介绍常用链接如下 1、收放卷+排线控制 收放卷+排线控制系统框图-CSDN博客文章浏览阅读24次。1、收放卷前馈量计算FC收放卷前馈量计算FC(CODESYS ST源代…

以太ETH链市值机器人

在数字资产交易市场的浪潮中&#xff0c;如何高效地管理市值、提升交易流动性并保障资金安全&#xff0c;一直是交易所和项目方关注的焦点。市值管理机器人飞机//aishutuyu以太ETH链市值机器人凭借其卓越的功能和强大的安全保障&#xff0c;为数字资产交易市场带来了革命性的变…

乡村旅游指标-最美乡村数、旅游示范县数、旅行社数、景区数、农家乐数(2007-2021年)

01、数据介绍 乡村旅游也是促进乡村经济发展的有效途径。通过发展乡村旅游&#xff0c;可以带动乡村相关产业的发展&#xff0c;提高乡村居民的收入&#xff0c;促进乡村的经济发展和社会进步。此外&#xff0c;乡村旅游还能促进城乡交流&#xff0c;推动城乡统筹发展。 数据…

【自动驾驶|毫米波雷达】非相参积累与恒虚警率检测

目录 非相参积累&#xff08;Non-coherent combing&#xff09; 1. 非相参积累是什么? 2. 为什么要进行非相参积累&#xff1f; 3. 如何实现非相参积累&#xff1f; 恒虚警率检测&#xff08;CFAR&#xff1a;Constant False Alarm Rate&#xff09; 1.CFAR概念引入 2. C…

几个Python处理Excel实际应用

下面介绍四个不同类型的Python处理Excel的经典应用案例&#xff0c;以帮助读者更好地掌握Python处理Excel的技能。 一、读取Excel数据 Python通过pandas库可以轻松地读取Excel数据。pandas库是一个专门用于数据分析和处理的库&#xff0c;它可以将Excel中的数据读取为DataFra…

【CTF MISC】XCTF GFSJ0513 pdf Writeup(PDF隐写)

pdf 菜猫给了菜狗一张图&#xff0c;说图下面什么都没有 解法 打开 pdf&#xff0c;只看见一张图片。 用浏览器搜索 flag&#xff0c;发现图片中间藏了一行字。 复制出来&#xff0c;得到 flag。 Flag flag{security_through_obscurity}声明 本博客上发布的所有关于网络攻…

vivado Kintex-7 配置存储器器件

Kintex-7 配置存储器器件 下表所示闪存器件支持通过 Vivado 软件对 Kintex -7 器件执行擦除、空白检查、编程和验证等配置操作。 本附录中的表格所列赛灵思系列非易失性存储器将不断保持更新 &#xff0c; 并支持通过 Vivado 软件对其中所列非易失性存储器 进行擦除、…

Apache Flume概述

Apache Flume概述 1.Flume定义 ​ Flume是cloudera(CDH版本的hadoop) 开发的一个分布式、可靠、高可用的海量日志收集系统。 它将各个服务器中的数据收集起来并送到指定的地方去&#xff0c;比如说送到HDFS、Hbase&#xff0c;简单来说flume就是收集日志的。 2.Flume基础架构…

用websocket实现一个简单的im聊天功能

WebSocket前后端建立以及使用-CSDN博客 经过我上一篇的基本理解websocket的建立以及使用后&#xff0c;这篇就写一个简单的demo 实现im聊天 首先就是后端代码&#xff0c;详细解释我都放到了每一句的代码解析了&#xff0c;文章最后我会说怎么运行流程 放置后端代码 packa…