双向带头循环链表-----重置版
创始人
2025-05-30 13:42:53

我们本次来完成双向带头循环链表,与之前的内容相比增加了些许细节,大家也可以看看之前写的

(1条消息) 链表---双向带头循环链表及其多种接口的实现(完美结构)-CSDN博客

 

目录

链表结构

尾插

创建新节点

初始化

打印数据

判断是否为空

尾删

头插

头删

pos位置之前插入

删除pos位置节点

查找节点

销毁

全部代码


链表结构

typedef int LTDataType;
typedef struct ListNode {struct ListNode* next;struct ListNode* prev;LTDataType data;
}LTNode;

由于是双向链表,所以相比单链表,多了一个指针prev用来指向前一个节点

尾插

void LTPushBack(LTNode* phead, LTDataType x) {assert(phead);LTNode* newnode = BuyListNode(x);LTNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}

双向循环链表的尾插非常简单,我们要找到链表的尾节点,只需找头的prev即可,接着改变指向即可,这里对比单链表发现,这里不需要二级指针,我们这里改的都是结构体的变量,要修改结构体的变量,使用结构体的指针即可,这也是哨兵位头节点的优势,我们还要进行断言,因为存在哨兵位的头节点,所以是不可能为空,所以需要断言

创建新节点

LTNode* BuyListNode(LTDataType x) {LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL) {perror("malloc fail");return NULL;}node->next = NULL;node->prev = NULL;node->data = x;return node;
}

在尾插里我们使用了BuyListNode函数,所以我们这里进行实现,我们将新节点的next和prev都置为空,防止出现问题,并且我们发现,这个函数是有返回值的,这里如果不用返回值就会出现和单链表那里一样的问题,即形参和实参,那样就需要使用二级指针,所以我们这里用返回值解决即可

初始化

LTNode* LTInit() {LTNode* phead = BuyListNode(-1);phead->next = phead;phead->prev = phead;return phead;
}

初始化的问题是一样的,我们使用返回值来解决,初始化是创建哨兵位的头节点,我们让他的next和prev都指向自己,这样最初的带头双向循环链表就完成了,哨兵位的头节点不存储数据,我们这里随便给个-1

打印数据

void LTPrint(LTNode* phead) {assert(phead);printf("<-head<->");LTNode* cur = phead->next;while (cur != phead) {printf("%d<->", cur->data);cur = cur->next;}printf("\n");
}

既然链表是带哨兵位的,那么说明绝对不会为空,所以这些函数都需要断言,之后就不再说了,打印数据我们要从phead的next开始打印,这是因为哨兵位的头节点是不存储数据的,接着我们进行打印即可,因为是循环链表,当cur等于phead时,所有的数据也就被打印完了

有了这几个函数,我们简单测试一下

判断是否为空

bool LTEmpty(LTNode* phead) {assert(phead);return phead->next == phead;
}

 判断链表是否为空的条件是phead的next或者prev是否等于自己

尾删

void LTPopBack(LTNode* phead) {assert(phead);assert(!LTEmpty(phead));LTNode* tail = phead->prev;LTNode* tailPrev = tail->prev;tailPrev->next = phead;phead->prev = tailPrev;free(tail);tail = NULL;
}

尾删我们还要判断链表是否为空,链表为空时我们不能进行尾删,我们可以用严厉的检查也可以用温柔的,这里大家自行选择,我们使用两个指针找到尾节点和尾节点的prev,接着改变指向,释放尾节点即可

头插

void LTPushFront(LTNode* phead, LTDataType x) {assert(phead);LTNode* newnode = BuyListNode(x);/*newnode->next = phead->next;newnode->next->prev = newnode;newnode->prev = phead;phead->next = newnode;*/LTNode* first = phead->next;phead->next = newnode;newnode->prev = phead;first->prev = newnode;newnode->next = first;
}

我们使用一个变量first记录phead的next,接着改变指向即可

屏蔽的代码是另一种写法,不过这种写法不如用一个新变量直观

头删

void LTPopFront(LTNode* phead) {assert(phead);assert(!LTEmpty(phead));LTNode* first = phead->next;LTNode* second = first->next;second->prev = phead;phead->next = second;free(first);first = NULL;
}

头删与尾删类似,我们使用两个变量就可以轻松解决,改变指向后释放空间即可

pos位置之前插入

void LTInsert(LTNode* pos, LTDataType x) {assert(pos);LTNode* newnode = BuyListNode(x);LTNode* prev = pos->prev;newnode->next = pos;pos->prev = newnode;prev->next = newnode;newnode->prev = prev;
}

我们用一个指针指向pos的prev,接着改变指向即可,有了这个函数,我们就可以进行复用,比如尾插,头插等等,我们都可以用这个函数来进行代替,比如头插我们就可以这样写

void LTPushFront(LTNode* phead, LTDataType x) {assert(phead);//LTNode* newnode = BuyListNode(x);/*newnode->next = phead->next;newnode->next->prev = newnode;newnode->prev = phead;phead->next = newnode;*//*LTNode* first = phead->next;phead->next = newnode;newnode->prev = phead;first->prev = newnode;newnode->next = first;*/LTInsert(phead->next, x);
}

尾插直接传phead即可,因为phead的prev就是尾

void LTPushBack(LTNode* phead, LTDataType x) {assert(phead);/*LTNode* newnode = BuyListNode(x);LTNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;*/LTInsert(phead,x);
}

删除pos位置节点

void LTErase(LTNode* pos) {assert(pos);LTNode* prev = pos->prev;LTNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);
}

用两个指针改变指向,释放空间即可 

同理,有了Erase,头删和尾删我们就可以进行复用

void LTPopFront(LTNode* phead) {assert(phead);assert(!LTEmpty(phead));/*LTNode* first = phead->next;LTNode* second = first->next;second->prev = phead;phead->next = second;free(first);first = NULL;*/LTErase(phead->next);
}
void LTPopBack(LTNode* phead) {assert(phead);assert(!LTEmpty(phead));/*LTNode* tail = phead->prev;LTNode* tailPrev = tail->prev;tailPrev->next = phead;phead->prev = tailPrev;free(tail);tail = NULL;*/LTErase(phead->prev);
}

查找节点

LTNode* LTFind(LTNode* phead, LTDataType x) {assert(phead);LTNode* cur = phead->next;while (cur != phead) {if (cur->data == x) {return cur;}cur = cur->next;}return NULL;
}

我们对链表进行遍历查找即可,没有找到返回NULL

销毁

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

使用两个指针记录遍历,最后释放哨兵位节点即可,我们发现,phead最后没置为空,这也是二级指针问题,我们让使用的人置为空即可

以上即为本期全部内容,希望大家可以有所收获,最后附上全部代码

全部代码

#include
#include
#include
#include
typedef int LTDataType;
typedef struct ListNode {struct ListNode* next;struct ListNode* prev;LTDataType data;
}LTNode;
LTNode* BuyListNode(LTDataType x);
LTNode* LTInit();
void LTPrint(LTNode* phead);
void LTDestroy(LTNode* phead);
bool LTEmpty(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);
LTNode* LTFind(LTNode* phead, LTDataType x);
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
#include"List.h"LTNode* BuyListNode(LTDataType x) {LTNode* node = (LTNode*)malloc(sizeof(LTNode));if (node == NULL) {perror("malloc fail");return NULL;}node->next = NULL;node->prev = NULL;node->data = x;return node;
}LTNode* LTInit() {LTNode* phead = BuyListNode(-1);phead->next = phead;phead->prev = phead;return phead;
}
void LTDestroy(LTNode* phead) {assert(phead);LTNode* cur = phead->next;while (cur != phead) {LTNode* next = cur->next;free(cur);cur = next;}free(phead);
}
void LTPrint(LTNode* phead) {assert(phead);printf("<-head<->");LTNode* cur = phead->next;while (cur != phead) {printf("%d<->", cur->data);cur = cur->next;}printf("\n");
}bool LTEmpty(LTNode* phead) {assert(phead);return phead->next == phead;
}void LTPushBack(LTNode* phead, LTDataType x) {assert(phead);/*LTNode* newnode = BuyListNode(x);LTNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;*/LTInsert(phead,x);
}
void LTPopBack(LTNode* phead) {assert(phead);assert(!LTEmpty(phead));/*LTNode* tail = phead->prev;LTNode* tailPrev = tail->prev;tailPrev->next = phead;phead->prev = tailPrev;free(tail);tail = NULL;*/LTErase(phead->prev);
}void LTPushFront(LTNode* phead, LTDataType x) {assert(phead);//LTNode* newnode = BuyListNode(x);/*newnode->next = phead->next;newnode->next->prev = newnode;newnode->prev = phead;phead->next = newnode;*//*LTNode* first = phead->next;phead->next = newnode;newnode->prev = phead;first->prev = newnode;newnode->next = first;*/LTInsert(phead->next, x);
}
void LTPopFront(LTNode* phead) {assert(phead);assert(!LTEmpty(phead));/*LTNode* first = phead->next;LTNode* second = first->next;second->prev = phead;phead->next = second;free(first);first = NULL;*/LTErase(phead->next);
}
void LTInsert(LTNode* pos, LTDataType x) {assert(pos);LTNode* newnode = BuyListNode(x);LTNode* prev = pos->prev;newnode->next = pos;pos->prev = newnode;prev->next = newnode;newnode->prev = prev;
}
void LTErase(LTNode* pos) {assert(pos);LTNode* prev = pos->prev;LTNode* next = pos->next;prev->next = next;next->prev = prev;free(pos);
}
LTNode* LTFind(LTNode* phead, LTDataType x) {assert(phead);LTNode* cur = phead->next;while (cur != phead) {if (cur->data == x) {return cur;}cur = cur->next;}return NULL;
}

如有错误,还请指正

相关内容

热门资讯

今年我省粮食产量达515.56... (来源:辽宁日报)转自:辽宁日报 图为在中储粮(盘锦)储运有限公司,装运粮食的重型卡车排起长队...
国家发展改革委部署促进投资止跌... (来源:辽宁日报)转自:辽宁日报 新华社北京12月13日电 (记者魏玉坤) 记者13日从全国发展和改...
江苏省实施《中华人民共和国森林... (来源:新华日报) 目 录 第一章 总则 第二章 森林、林木和林地权属管理...
姜堰数字化产品讲“活”理论 (来源:新华日报) □ 本报记者 卢佳乐 通讯员 姜宣 “王教授,您约我‘喝茶论道’,...
联合国维和部队在苏丹遇袭 6人... 转自:财联社【联合国维和部队在苏丹遇袭 6人死亡】财联社12月14日电,当地时间13日,苏丹武装部队...