🔥博客主页:小王又困了
📚系列专栏:数据结构
🌟人之为学,不日近则日退
❤️感谢大家点赞👍收藏⭐评论✍️
目录
一、双向链表
1.1带头双向循环链表的结构
二、链表的实现
📒2.1初始化
📒2.2尾插
📒2.3尾删
📒2.4头插
📒2.5头删
📒 2.6在pos位置之前插入
📒 2.7删除pos位置
🗒️前言:
在上一期中我们介绍了单链表,也做了一些练习题,在一些题中使用单链表会十分繁琐。因为单链表只能正着走,不能倒着走,例如:回文、逆置。本期我们将学习带头双向循环链表。
一、双向链表
1.1带头双向循环链表的结构
特点:带头双向循环链表结构最复杂,一般用在单独存储数据。结构虽然结构复杂,但是使用代码实现以后会发现结构会带来多优势,实现反而简单了。
二、链表的实现
📒2.1初始化
LTNode* BuyLTNode(LTDateType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc");exit(-1);}newnode->date = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}LTNode* LTInit()
{LTNode* phead = BuyLTNode(0);phead->next = phead;phead->prev = phead;return phead;
}
📒2.2尾插
带哨兵位的链表尾插时不用判断是否有节点,两种情况的插入相同,而且还不用传递二级指针。
void LTPushBack(LTNode* phead, LTDateType x) {assert(phead);LTNode* tail = phead->prev;LTNode* newnode = BuyLTNode(x);newnode->prev = tail;tail->next = newnode;newnode->next = phead;phead->prev = newnode; }
📒2.3尾删
在尾删时我们通过 assert(phead->next != phead); 判断链表是否有节点。同时这个代码就有普遍性,不用单独考虑剩一个节点的情况。
void LTPopBack(LTNode* phead) {assert(phead);assert(phead->next != phead);LTNode* tail = phead->prev;LTNode* prevtail = tail->prev;free(tail);prevtail->next = phead;phead->prev = prevtail; }
📒2.4头插
头删重要的是赋值的顺序,顺序错误会找不到后面的节点,导致内存泄漏。带哨兵位的链表不需要传递二级指针,因为改变的是结构体的变量。
void LTPushFront(LTNode* phead, LTDateType x) {assert(phead);LTNode* newnode = BuyLTNode(x);newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;newnode->prev = phead; }
📒2.5头删
我们可以多定义几个指针来保存后面节点的地址,这样就不会造成节点的丢失,不用考虑赋值的顺序,会更加方便。
void LTPopFront(LTNode* phead) {assert(phead);assert(phead->next != phead);LTNode* first = phead->next;LTNode* second = first->next;free(first);phead->next = second;second->prev = phead; }
📒 2.6在pos位置之前插入
void LTInsert(LTNode* pos, LTDateType x) {assert(pos);LTNode* newnode = BuyLTNode(x);LTNode* posprev = pos->prev;newnode->next = pos;pos->prev = newnode;newnode->prev = posprev;posprev->next = newnode; }
我们可以通过复用LTInsert,实现链表的头插和尾插,使程序更简单。
- 当pos传递的是phead->next,就可以实现头插
- 当pos传递的是phead,就可以实现尾插
//头插 void LTPushFront(LTNode* phead, LTDateType x) {assert(phead);LTInsert(phead->next, x); }//尾插 void LTPushBack(LTNode* phead, LTDateType x) {assert(phead);LTInsert(phead, x); }
📒 2.7删除pos位置
void LTErase(LTNode* pos) {assert(pos);LTNode* posprev = pos->prev;LTNode* posnext = pos->next;free(pos);posprev->next = posnext;posnext->prev = posprev; }
我们可以通过复用LTInsert,实现链表的头删和尾删,使程序更简单。
- 当pos传递的是phead->next,就可以实现头删
- 当pos传递的是phead->prev,就可以实现尾插删
//头删 void LTPopFront(LTNode* phead) {assert(phead);assert(phead->next != phead);LTErase(phead->next); }//尾删 void LTPopBack(LTNode* phead) {assert(phead);assert(phead->next != phead);LTErase(phead->prev); }
通过上面链表的实现,我们已经感受到了带头双向循环链表的方便和简单,它不需要去考虑链表是否有元素,还可以找到前一个元素,在我们使用中提供很大的便利。
本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位读者三连支持。文章有问题可以在评论区留言,博主一定认真认真修改,以后写出更好的文章。