数据结构-带头双向循环链表的实现

前言 

        带头双向循环链表是一种重要的数据结构,它的结构是很完美的,它弥补了单链表的许多不足,让我们一起来了解一下它是如何实现的吧!

1.节点的结构

        它的节点中存储着数据和两个指针,一个指针_prev用来记录前一个节点的地址,另一个指针_next 用来记录后一个节点的地址。

typedef int LTDataType;
typedef struct ListNode
{struct ListNode* _next;struct ListNode* _prev;LTDataType _data;
}ListNode;

2.尾部的插入和删除的实现

        由于这是带头循环的双向链表,所以尾插只需要在它的头结点的_prev 处进行插入,然后重新链接就可以了。如图: 

        如果只有一个头结点,插入也是一样的。

void ListPushBack(ListNode* phead, LTDataType data)//尾插
{assert(phead);ListNode* newNode = BuyListNode(data);//申请新节点ListNode* tail = phead->_prev;//找尾结点//链接新节点和尾结点tail->_next = newNode;newNode->_prev = tail;//与头结点进行链接phead->_prev = newNode;newNode->_next = phead;
}

         尾部的删除,首先需要找到链表的尾和尾的前一个节点,删除尾结点之后,将前一个节点进与头结点进行链接,如图:

void ListPopBack(ListNode* phead)//尾删除
{//确保指针不为空assert(phead);assert(phead->_next != phead);//保留头结点//找尾ListNode* tail = phead->_prev;ListNode* newTail = tail->_prev;//找到新的尾结点//删除旧的尾结点free(tail);//重新链接头尾节点newTail->_next = phead;phead->_prev = newTail;
}

3.头部插入和删除的实现

        头部的插入,删除和尾部的插入,删除类似,需要注意的是删除的不是 头结点,是存储有效数据的第一个节点,插入数据也是在第一个有效数据节点和头结点之间插入。如图:

 

void ListPushFront(ListNode* phead, LTDataType data)//头插
{//确保指针不为空assert(phead);//申请新的节点ListNode* newNode = BuyListNode(data);//进行链接ListNode* realHead = phead->_next;realHead->_prev = newNode;newNode->_next = realHead;phead->_next = newNode;newNode->_prev = phead;
}
void ListPopFront(ListNode* phead)//头删插
{//指针存在assert(phead);//并且确保不能删除头结点assert(phead->_next != phead);//找到真正的头ListNode* realHead = phead->_next;ListNode* realHeadNext = realHead->_next;//删除头节点free(realHead);//重新链接phead->_next = realHeadNext;realHeadNext->_prev = phead;
}

4.任意位置的插入和删除 

        在任意位置进行插入和删除,需要知道节点的指针,插入或者删除节点之后需要 更新节点的链接关系。如图:

 

void ListInsert(ListNode* pos, LTDataType data)//pos位置之前的插入
{assert(pos);//确保指针有效ListNode* newNode = BuyListNode(data);//申请节点//进行链接ListNode* prev = pos->_prev;ListNode* next = pos;prev->_next = newNode;newNode->_prev = prev;newNode->_next = next;next->_prev = newNode;
}
void ListErase(ListNode* pos)//pos位置的删除
{//确保指针有效assert(pos);ListNode* next = pos->_next;ListNode* prev = pos->_prev;//删除pos所指向的节点free(pos);//进行重新链接prev->_next = next;next->_prev = prev;
}

         对任意位置的插入和删除进行测试时,可以通过复用接口来实现,头插尾插,头删尾删都可以调用这两个接口来实现,如下:

void ListPushBack(ListNode* phead, LTDataType data)//尾插
{ListInsert(phead, data);
}
void ListPopBack(ListNode* phead)//尾删除
{ListErase(phead->_prev);
}
void ListPushFront(ListNode* phead, LTDataType data)//头插
{ListInsert(phead->_next,data);
}
void ListPopFront(ListNode* phead)//头删插
{ListErase(phead->_next);
}

5.链表的初始化和删除

        带头的双向循环链表初始化的时候就需要申请内存给头节点。 

ListNode* BuyListNode(LTDataType data)//申请节点
{ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));if (newNode == NULL){printf("申请空间失败\n");exit(-1);}newNode->_data = data;return newNode;
}
void ListInit(ListNode** pphead)//初始化链表
{*pphead = BuyListNode(0);//申请头结点//并且初始化(*pphead)->_next = *pphead;(*pphead)->_prev = *pphead;
}
//清理链表
void ListClear(ListNode* phead)
{assert(phead);//确保链表不为空assert(phead->_next != phead);//除了确保不清理头结点ListNode* cur = phead->_next;while (cur != phead){ListNode* clearNode = cur;cur = cur->_next;free(clearNode);}
}
void ListDestory(ListNode** ppHead)//摧毁链表
{assert(*ppHead);//确保指针不为空ListClear(*ppHead);free(*ppHead);//释放头结点
}

6.查找并修改链表的节点的数据

        查找和修改是一起的,实现查找就可以实现 修改链表的值。

ListNode* ListFind(ListNode* phead, LTDataType data)//查找链表
{assert(phead);ListNode* cur = phead->_next;while (cur != phead){if (cur->_data == data)return cur;cur = cur->_next;}return NULL;//找不到返回NULL
}
void ListTest4()
{//查找和修改的测试ListNode* pHead = NULL;ListInit(&pHead);ListPushFront(pHead, 1);ListPushFront(pHead, 2);ListPushFront(pHead, 3);ListPushFront(pHead, 4);ListPushFront(pHead, 5);ListPushFront(pHead, 6);ListNode* node = ListFind(pHead, 3);//在链表中查找if (node){//修改链表的值node->_data = 90;}ListPrint(pHead);ListDestory(&pHead);
}

7.全部代码

//List.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{struct ListNode* _next;struct ListNode* _prev;LTDataType _data;
}ListNode;void ListPushBack(ListNode* phead, LTDataType data);//尾插void ListPopBack(ListNode* phead);//尾删除
void ListPushFront(ListNode* phead,LTDataType data);//头插
void ListPopFront(ListNode* phead);//头删插
ListNode* BuyListNode(LTDataType data);//申请节点
void ListInit(ListNode** pphead);//初始化链表void ListInsert(ListNode* pos, LTDataType data);//pos位置之前的插入void ListErase(ListNode* pos);//pos位置的删除
//清理链表
void ListClear(ListNode* phead);void ListDestory(ListNode** ppHead);//摧毁链表void ListPrint(ListNode* phead);//打印链表
ListNode* ListFind(ListNode* phead, LTDataType data);//查找链表

 //List.c

#include"List.h"
void ListPushBack(ListNode* phead, LTDataType data)//尾插
{assert(phead);ListNode* newNode = BuyListNode(data);//申请新节点ListNode* tail = phead->_prev;//找尾结点//链接新节点和尾结点tail->_next = newNode;newNode->_prev = tail;//与头结点进行链接phead->_prev = newNode;newNode->_next = phead;
}
void ListPopBack(ListNode* phead)//尾删除
{//确保指针不为空assert(phead);assert(phead->_next != phead);//保留头结点//找尾ListNode* tail = phead->_prev;ListNode* newTail = tail->_prev;//找到新的尾结点//删除旧的尾结点free(tail);//重新链接头尾节点newTail->_next = phead;phead->_prev = newTail;
}
void ListPushFront(ListNode* phead, LTDataType data)//头插
{//确保指针不为空assert(phead);//申请新的节点ListNode* newNode = BuyListNode(data);//进行链接ListNode* realHead = phead->_next;realHead->_prev = newNode;newNode->_next = realHead;phead->_next = newNode;newNode->_prev = phead;
}
void ListPopFront(ListNode* phead)//头删插
{//指针存在assert(phead);//并且确保不能删除头结点assert(phead->_next != phead);//找到真正的头ListNode* realHead = phead->_next;ListNode* realHeadNext = realHead->_next;//删除头节点free(realHead);//重新链接phead->_next = realHeadNext;realHeadNext->_prev = phead;
}
ListNode* BuyListNode(LTDataType data)//申请节点
{ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));if (newNode == NULL){printf("申请空间失败\n");exit(-1);}newNode->_data = data;return newNode;
}
void ListInit(ListNode** pphead)//初始化链表
{*pphead = BuyListNode(0);//申请头结点//并且初始化(*pphead)->_next = *pphead;(*pphead)->_prev = *pphead;
}
void ListPrint(ListNode* phead)//打印链表
{assert(phead);ListNode* cur = phead->_next;while (cur != phead){printf("%d ", cur->_data);cur = cur->_next;}printf("\n");
}
void ListInsert(ListNode* pos, LTDataType data)//pos位置之前的插入
{assert(pos);//确保指针有效ListNode* newNode = BuyListNode(data);//申请节点//进行链接ListNode* prev = pos->_prev;ListNode* next = pos;prev->_next = newNode;newNode->_prev = prev;newNode->_next = next;next->_prev = newNode;
}
void ListErase(ListNode* pos)//pos位置的删除
{//确保指针有效assert(pos);ListNode* next = pos->_next;ListNode* prev = pos->_prev;//删除pos所指向的节点free(pos);//进行重新链接prev->_next = next;next->_prev = prev;
}
//清理链表
void ListClear(ListNode* phead)
{assert(phead);//确保链表不为空assert(phead->_next != phead);//除了确保不清理头结点ListNode* cur = phead->_next;while (cur != phead){ListNode* clearNode = cur;cur = cur->_next;free(clearNode);}
}
void ListDestory(ListNode** ppHead)//摧毁链表
{assert(*ppHead);//确保指针不为空ListClear(*ppHead);free(*ppHead);//释放头结点
}
ListNode* ListFind(ListNode* phead, LTDataType data)//查找链表
{assert(phead);ListNode* cur = phead->_next;while (cur != phead){if (cur->_data == data)return cur;cur = cur->_next;}return NULL;//找不到返回NULL
}

//test.c

#include"List.h"
void ListTest1()
{//尾插尾删的测试代码ListNode* pHead = NULL;ListInit(&pHead);ListPushBack(pHead, 1);ListPushBack(pHead, 2);ListPushBack(pHead, 3);ListPushBack(pHead, 4);ListPushBack(pHead, 5);ListPushBack(pHead, 6);ListPrint(pHead);ListPopBack(pHead);ListPopBack(pHead);ListPopBack(pHead);ListPopBack(pHead);ListPopBack(pHead);ListPopBack(pHead);//ListPopBack(pHead);}
void ListTest2()
{//头插头删的测试ListNode* pHead = NULL;ListInit(&pHead);ListPushFront(pHead, 1);ListPushFront(pHead, 2);ListPushFront(pHead, 3);ListPushFront(pHead, 4);ListPushFront(pHead, 5);ListPushFront(pHead, 6);ListPrint(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);}
void ListTest3()
{//Destory和Clear的测试ListNode* pHead = NULL;ListInit(&pHead);ListPushFront(pHead, 1);ListPushFront(pHead, 2);ListPushFront(pHead, 3);ListPushFront(pHead, 4);ListPushFront(pHead, 5);ListPushFront(pHead, 6);ListDestory(&pHead);
}
int main()
{ListTest3();return 0;
}

 

8.测试代码

void ListTest1()
{//尾插尾删的测试代码ListNode* pHead = NULL;ListInit(&pHead);ListPushBack(pHead, 1);ListPushBack(pHead, 2);ListPushBack(pHead, 3);ListPushBack(pHead, 4);ListPushBack(pHead, 5);ListPushBack(pHead, 6);ListPrint(pHead);ListPopBack(pHead);ListPopBack(pHead);ListPopBack(pHead);ListPopBack(pHead);ListPopBack(pHead);ListPopBack(pHead);//ListPopBack(pHead);}
void ListTest2()
{//头插头删的测试ListNode* pHead = NULL;ListInit(&pHead);ListPushFront(pHead, 1);ListPushFront(pHead, 2);ListPushFront(pHead, 3);ListPushFront(pHead, 4);ListPushFront(pHead, 5);ListPushFront(pHead, 6);ListPrint(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);ListPopFront(pHead);}
void ListTest3()
{//Destory和Clear的测试ListNode* pHead = NULL;ListInit(&pHead);ListPushFront(pHead, 1);ListPushFront(pHead, 2);ListPushFront(pHead, 3);ListPushFront(pHead, 4);ListPushFront(pHead, 5);ListPushFront(pHead, 6);ListDestory(&pHead);
}

 

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

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

相关文章

如何通知学生分班结果?这个代码源轻松搞定

传统通知学生分班结果的方式相对来说比较繁琐&#xff0c;同时也存在出错的风险。首先&#xff0c;传统方式通常是通过纸质通知单的形式将分班结果发放给学生。这就需要学校花费一定的人力和物力来打印、分发通知单&#xff0c;而且还需要确保通知单能够准确地送到每个学生手中…

OpenAI允许网站阻止其网络爬虫;谷歌推出类似Grammarly的语法检查功能

&#x1f989; AI新闻 &#x1f680; OpenAI推出新功能&#xff0c;允许网站阻止其网络爬虫抓取数据训练GPT模型 摘要&#xff1a;OpenAI最近推出了一个新功能&#xff0c;允许网站阻止其网络爬虫从其网站上抓取数据训练GPT模型。该功能通过在网站的Robots.txt文件中禁止GPTB…

图像的镜像变换之c++实现(qt + 不调包)

1.基本原理 1.水平镜像变化 设图像的宽度为width&#xff0c;则水平镜像变化的映射关系如下&#xff1a; 2.垂直镜像变化 设图像的宽度为height&#xff0c;则垂直镜像变化的映射关系如下&#xff1a; 2.代码实现&#xff08;代码是我以前自学图像处理时写的&#xff0c;代码很…

Nature子刊 |肠道宏病毒组揭示百岁老人长寿秘诀

发表期刊&#xff1a;nature microbiology 发表时间&#xff1a;2023 影响因子&#xff1a;28.3 DOI: 10.1038/s41564-023-01370-6 研究背景 衰老是一种不可逆转的自然过程&#xff0c;随着年龄的增长&#xff0c;机体诸多方面出现功能性下降&#xff0c;与衰老相关的疾病&a…

设计模式之三大类

目录 设计模式分类 1.创建型模式(Creational Patters) 2.结构型模式(Structural Patterns) 3.行为型模式(Behavioral Patterns) 3.1命令模式(The Command Pattern) 2.1适配器模式 2.1.1Object and Class Adapters 设计模式分类 1.创建型模式(Creational Patters) Fato…

手撕数据结构之栈+例题

目录 一、栈的概念及结构 二、栈的头文件及基本框架 三、接口实现 1、对栈的初始化 2、栈的销毁 3、入栈操作 4、出栈操作 5、判断栈是否为空 6、返回栈顶元素 7、遍历栈 四、有效的括号 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 思路&#xff…

Falco操作系统安全威胁监测利器

原理简介 Falco是一个开源的云原生安全工具&#xff0c;用于检测和防御容器和云原生环境中的安全威胁。它基于Linux内核的eBPF技术&#xff0c;通过监控系统调用和内核事件来实现安全检测和响应。 具体来说&#xff0c;Falco的实现原理如下&#xff1a; 1. 内核模块&#xf…

如何使用Spark/Flink等分布式计算引擎做网络入侵检测

如何使用Spark/Flink等分布式计算引擎做网络入侵检测 引言16 Distributed Abnormal Behavior Detection Approach Based on Deep Belief Network and Ensemble SVM Using Spark17 Spark configurations to optimize decision tree classification on UNSW-NB1518 A dynamic spa…

Windows Oracle21C与PLSQL Developer 15配置

1、下载Oracle21c并安装 下载地址&#xff1a;https://www.oracle.com/database/technologies/oracle21c-windows-downloads.html 2、下载PLSQL Developer 15并安装 下载地址&#xff1a;https://www.allroundautomations.com/products/pl-sql-developer/#pricing 3、配置O…

drawio导出矢量图

1.选中要导出的图 2.导出为pdf 3.用adobe打开pdf&#xff0c;另存为eps

NLP文本分类

NLP文本分类 落地实战五大利器&#xff01;_kaiyuan_sjtu的博客-CSDN博客https://zhuanlan.zhihu.com/p/432619164 https://github.com/alibaba/EasyNLP/blob/master/README.cn.md

Lua学习记录

Lua基础了解 Lua的注释通过 (-- 单行注释&#xff0c;--[[ ]] 多行注释)可以不加&#xff1b; 多个变量赋值&#xff0c;按顺序赋值&#xff0c;没有则为nil&#xff1b; function的简单用法&#xff0c;多个返回值配合多重赋值&#xff0c;以end为结束标志 Lua下标从1开始&…