链表最终章双向链表及其应用
1245 字
6 分钟
链表最终章双向链表及其应用
———————————本文旨在交流探讨计算机知识,欢迎交流指正————————————
上一章,我们介绍了链表的循环扩展,但是,单向链表毕竟是单向查询的,就算是经过循环来查找,终究是效率偏低,下面,我们来介绍一种工程项目中运用更加广泛的一种链表结构:双向链表——:
根据此结构,我们可以发现,虽然代码复杂度增加了,但是由于此结构是双向的,操作更加便捷,下面,我将演示各模块内容:
首先,是结构的构建:
typedef int Element;typedef struct _node {Element val;struct _node *next;struct _node *prev;} DNode, DList;
/* 使用一个带头节点的双向循环链表,头节点让用户来管理,提供初始化接口 */void initDList(DList *header);void releaseDList(DList *header);
// 实现头插、尾插void insertDListHeader(DList *header, Element val);void insertDListRear(DList *header, Element val);// 显示链表void showDList(const DList *header);// 删除一个元素void delDList(DList *header, Element e);注:此为.h头文件里的内容,若先用头文件封装再放在.c文件里面使用,最后在main.c文件里面测试,很好的保证了产品内容的安全性与代码便捷性。 1、这一次,为了让代码更加简洁规范,我们使用static的模式构建加入和删除两大基本模块:
//next是已知的待插入位置节点的下一个节点;//prev是已知的待插入节点的上一个节点(前置节点);
//插入static void addDNode(DNode *new_node, DNode *prev, DNode *next) {next->prev = new_node;//待插入节点下一个节点的上一个节点是新节点;new_node->next = next;//新节点的下一个是上一个节点;new_node->prev = prev;新节点上一个是前置节点prev->next = new_node;前置节点下一个是新节点}
//删除static void delDNode(DNode *prev, DNode *next) {next->prev = prev;//某节点下一个节点的前一个是某节点上一个节点;prev->next = next;//某节点上一个节点的下一个是某节点下一个节点;}2、然后,我们再在此基础上完成增删改查的基本操作:
1、初始化:
void initDList(DList* header) {header->val = 0;//初始化头结点的数值域为0header->next = header->prev = header;//头结点的前置节点和后置节点都初始化指向头结点}
2、增添操作:
void insertDListHeader(DList* header, Element val) {DNode *new_node = malloc(sizeof(DNode));//malloc申请空间建立一个新节点new_node->val = val;//新节点赋值;addDNode(new_node, header, header->next);//函数操作++header->val;}//头插法;void insertDListRear(DList* header, Element val) {DNode *new_node = malloc(sizeof(DNode));new_node->val = val;addDNode(new_node, header->prev, header);++header->val;}//尾插法;
3、删除操作
void delDList(DList* header, Element e) {// 1. 找到这个元素,就可以删除,不需要再找到前置节点DNode *pos = header->next;while (pos != header && pos->val != e) {pos = pos->next;}// 2. 找到没有?if (pos != header) { // 找到delDNode(pos->prev, pos->next);pos->next = pos->prev = NULL;free(pos);--header->val;} else {printf("Not find %d element!\n", e);}
4、展示函数(若对工程有疑问和对const有疑问可以看本专栏的第一篇内容)
void showDList(const DList* header) {DNode *pos = header->next;printf("show: ");while (pos != header) {printf("\t%d", pos->val);pos = pos->next;}printf("\n");}//简单的循环打印,不过多赘述;
5、最后,是销毁链表的函数:
void releaseDList(DList* header) {DNode *pos = header->next;DNode *tmp = NULL;while (pos != header) {tmp = pos;delDNode(pos->prev, pos->next);pos = pos->next;free(tmp);--header->val;}}//逐个销毁
最后附上各文件代码: 1、doubleList.h:
#ifndef DOUBLE_LIST_H#define DOUBLE_LIST_H
typedef int Element;typedef struct _node {Element val;struct _node *next;struct _node *prev;} DNode, DList;
/* 使用一个带头节点的双向循环链表,头节点让用户来管理,提供初始化接口 */void initDList(DList *header);void releaseDList(DList *header);
// 实现头插、尾插void insertDListHeader(DList *header, Element val);void insertDListRear(DList *header, Element val);// 显示链表void showDList(const DList *header);// 删除一个元素void delDList(DList *header, Element e);
#endif //DOUBLE_LIST_H2、doubleList.c
#include "doubleList.h"//doubleList.h文件封装的函数名#include#include
static void addDNode(DNode *new_node, DNode *prev, DNode *next) {next->prev = new_node;new_node->next = next;new_node->prev = prev;prev->next = new_node;}
static void delDNode(DNode *prev, DNode *next) {next->prev = prev;prev->next = next;}
void initDList(DList* header) {header->val = 0;header->next = header->prev = header;}
void releaseDList(DList* header) {DNode *pos = header->next;DNode *tmp = NULL;while (pos != header) {tmp = pos;delDNode(pos->prev, pos->next);pos = pos->next;free(tmp);--header->val;}}
void insertDListHeader(DList* header, Element val) {DNode *new_node = malloc(sizeof(DNode));new_node->val = val;addDNode(new_node, header, header->next);++header->val;}
void insertDListRear(DList* header, Element val) {DNode *new_node = malloc(sizeof(DNode));new_node->val = val;addDNode(new_node, header->prev, header);++header->val;}
void showDList(const DList* header) {DNode *pos = header->next;printf("show: ");while (pos != header) {printf("\t%d", pos->val);pos = pos->next;}printf("\n");}
void delDList(DList* header, Element e) {// 1. 找到这个元素,就可以删除,不需要再找到前置节点DNode *pos = header->next;while (pos != header && pos->val != e) {pos = pos->next;}// 2. 找到没有?if (pos != header) { // 找到delDNode(pos->prev, pos->next);pos->next = pos->prev = NULL;free(pos);--header->val;} else {printf("Not find %d element!\n", e);}}3、main.c
#include#include "doubleList.h"
DList stu_table;
int main() {initDList(&stu_table);
for (int i = 0; i < 5; ++i) {// insertDListHeader(&stu_table, i + 100);insertDListRear(&stu_table, i + 100);}insertDListHeader(&stu_table, 60);insertDListHeader(&stu_table, 80);showDList(&stu_table);printf("====================\n");delDList(&stu_table, 102);showDList(&stu_table);releaseDList(&stu_table);printf("num: %d\n", stu_table.val);showDList(&stu_table);return 0;}————————————希望能对你有所帮助!!!————————————————
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!
相关文章 智能推荐
1
拓展单向循环链表
数据结构 —————————————本文旨在讨论计算机知识欢迎指正——————————————— 书接上回:我们已经了解了链表如何编写与前置节点和头指针两种表示方式,下面,我们来了解进阶写法———如何实现单向循环链表。 下面,我们来梳理一下循环链表的实现方式: 这是朴素的链表实现形式:
这是我们理想中的循环...
2
链表的实现与介绍
数据结构 ——————————————本文旨在交流计算机知识,欢迎指正————————————— 首先,我们回顾一下上一期讲的顺序表: 顺序表是一种物理结构上连续的,一整块连续空间的逻辑表,但是,试想一下,如果每次我们都是申请在一块整块空间里面的顺序表,但是我们并不知道堆空间里面我们malloc一块空间之后我...
3
附录对于头结点单向链表的优化方法
数据结构 ————————————本文旨在交流学习计算机知识,欢迎交流讨——————————— 上一章,我们浅显的讨论了链表的结构,本文在上一章的链表的基础上做出了些许优化,可配合食用。 回到正题,我们已经了解链表的结构,它是一种非连续空间存储的线性表,主要依靠地址域访问下一个元素并通过遍历操作进行增删改查。...
4
顺序表的实现与介绍及部分工程思想的思考
数据结构 ——————————本文旨在讨论和探究计算机知识,欢迎指正———————————— 首先,我们来聊聊什么是线性表: 线性表,顾名思义,是一种具有线性结构的数据排布形式 ,是一个类似数组的东西,在逻辑上一个接一个有序的整块链接,区别于链表的无序分布链接。
如图,我们可以得出,顺序表是一条线一样的结构...
5
数据结构之树及树的存储
数据结构 ——————————————本栏目旨在交流计算机知识————————————————— 上一章节,我们介绍了线性的数据结构:表,栈,队列。接下来,我们将进入下一章节——树结构的学习。 首先,我们先介绍一下树的基本概念: 结点:使⽤树结构存储的每一个数据元素都被称为“结点”。例如图中的A就是一个结点。...
随机文章 随机推荐