网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
LNode* tailprev = tail->prev; //找到尾的前驱 tailprev->next = phead; //尾前驱节点的后继指针指向头 phead->prev = tail->prev; //头的前驱指针指向尾的前驱 free(tail); tail = NULL;
}
* 测试尾删接口功能实现: ![](https://img-blog.csdnimg.cn/5048ad503c914acdbcfbb926f5fb67cd.png)
Ⅵ.双向链表头插:
- 在执行操作前应当对传入指针进行判断,防止传入空指针。
- 在改变指向前,应当首先保存哨兵节点的后继节点,因为当我们插入新节点后链表结构将会发生改变,再想要找到该节点将变得麻烦。
- 进行插入操作时,使哨兵节点的后继指针指向新节点,再使新节点的前驱指针指向哨兵节点,接着使用同样的操作使新节点与哨兵节点的原后继节点互指。
void LPushFront(LNode* phead, LDataType x) { if (phead == NULL) { return; } LNode* newnode = BuyListNode(x); LNode* next = phead->next; //保存哨兵节点的后继节点 phead->next = newnode; //哨兵点的后继指针指向新节点 newnode->prev = phead; //新节点的前驱指针指向哨兵节点 newnode->next = next; //新节点的后继指针指向哨兵节点的原后继节点 next->prev = newnode; //哨兵节点的原后继节点前驱指针指向新节点 }
- 测试头插接口功能实现:
Ⅶ.双向链表头删:
- 首先**进行非空判断,并排除链表为空(只含有哨兵节点)**的情况。
- 开始进行头删操作前找到并保存头节点,防止改变指向后链表结构发生变化难以找到原本的头节点,以便于最后进行释放。
- 并且应当找到并保存原头节点的后继节点,防止防止改变指向后链表结构发生变化而难以找到。
- 具体操作便是使哨兵节点与头节点的后继节点跳过头节点互指,再将要删除的头节点释放并置空即可。
void LPopFront(LNode* phead) { if ((phead == NULL) || (phead->next == phead)) //排除为空的情况 { return; } LNode* next = phead->next; //保存头节点,便于释放 LNode* nextNext = next->next; //保存头节点的后继节点 phead->next = nextNext; nextNext->prev = phead; free(next); next = NULL; }
- 测试头删接口功能实现:
Ⅷ.双向链表查找:
- 执行操作前需进行非空判断,防止对空指针进行操作。
- 查找逻辑很简单,遍历整个链表,直至链表完整循环一遍,若比对存在匹配元素返回该节点,否则返回空。
LNode* LFind(LNode* phead, LDataType x) { if (phead == NULL) { return; } LNode* cur = phead->next; while (cur != phead) { if (cur->data == x) { return cur; } else { cur = cur->next; } } return NULL; }
- 测试查找接口功能实现:
Ⅸ.双向链表给定节点前插:
- 执行操作前需进行非空判断,防止对空指针进行操作。
- 执行逻辑很简单,将数据存入通过动态申请的来的新节点中后,找到目标节点,使目标节点的前驱节点与新节点互指,再使新节点与目标节点互指接即可。
void LInsert(LNode* pos, LDataType x) { if (pos == NULL) { return; } LNode* posPrev = pos->prev; LNode* newnode = BuyListNode(x); posPrev->next = newnode; newnode->prev = posPrev; newnode->next = pos; pos->prev = newnode; }
- 测试前插接口功能实现:
Ⅹ.双向链表给定节点后插:
- 执行操作前需进行非空判断,防止对空指针进行操作。
- 执行逻辑与前插高度类似,不同的是使目标节点的后继节点与新节点互指,再使新节点与目标节点互指。
void LInsertBack(LNode* pos, LDataType x) { if (pos == NULL) { return; } LNode* posPrev = pos->next; LNode* newnode = BuyListNode(x); posPrev->prev = newnode; newnode->next = posPrev; newnode->prev = pos; pos->next = newnode; }
- 测试后插接口功能实现:
ⅩⅠ.双向链表删除给定节点:
- 执行操作前需进行非空判断,防止操作空指针。
void LErase(LNode* pos) { if (pos == NULL) { return; } LNode* posPrev = pos->prev; LNode* posNext = pos->next; posPrev->next = posNext; posNext->prev = posPrev; free(pos); pos = NULL; }
- 测试删除接口功能实现:
ⅩⅡ.双向链表销毁:
- 若哨兵节点为空,即表示链表内没有有效数据节点,则无需进行销毁、释放与置空操作。
- 含有有效节点则遍历所有节点,将每一个节点均进行释放,特别注意所有数据节点释放完毕之后,不要忘记释放哨兵节点。
void LDestroy(LNode* phead) { if(phead==NULL) { return; } LNode* cur = phead->next; while (cur != phead); { LNode* next = cur->next; free(cur); cur = next; } free(phead); phead = NULL; }
🍄三、完整接口实现代码🍄:
1.List.h:
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//自定数据类型LDataType:
typedef int LDataType;
//双向链表节点结构:
typedef struct ListNode
{
LDataType data;
struct LNode* next;
struct LNode* prev;
}LNode;
LNode* LInit(); //初始化双向循环链表
void LPrint(LNode* phead); //打印双向循环链表
LNode* BuyListNode(LDataType x);//双向循环链表新节点申请
void LPushBack(LNode* phead, LDataType x); //双向循环链表尾插
void LPopBack(LNode* phead); //双向循环链表尾删
void LPushFront(LNode* phead, LDataType x); //双向循环链表头插
void LPopFront(LNode* phead); //双向循环链表头删
LNode* LFind(LNode* phead, LDataType x); //双向循环链表查找
void LInsertFront(LNode* pos, LDataType x); //双向循环链表给定节点前插
void LInsertBack(LNode* pos, LDataType x); //双向循环链表给定节点后插
void LErase(LNode* pos); //双向循环链表给定节点删除
void LDestroy(LNode* phead); //双向循环链表的销毁
2.List.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//初始化双向循环链表初始化:
LNode* LInit()
{
//哨兵位头节点:
LNode* phead = (LNode*)malloc(sizeof(LNode));
phead->next = phead;
phead->prev = phead;
return phead;
}
//打印双向循环链表
void LPrint(LNode* phead)
{
if (phead == NULL)
{
return;
}
LNode* cur = phead->next;
while (cur != phead)
{
printf("%d -> ", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
//双向循环链表申请新节点:
LNode* BuyListNode(LDataType x)
{
LNode* newnode = (LNode*)malloc(sizeof(LNode));
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
//双向循环链表尾插:
void LPushBack(LNode* phead, LDataType x)
{
if (phead == NULL)
{
return;
}
LNode* tail = phead->prev; //找到尾节点
LNode* newnode = BuyListNode(x);
//新尾互指:
tail->next = newnode;
newnode->prev = tail;
//新头互指:
phead->prev = newnode;
newnode->next = phead;
}
//双向循环链表尾删:
void LPopBack(LNode* phead)
{
if ((phead == NULL) || (phead->next == phead)) //排除为空的情况
{
return;
}
LNode* tail = phead->prev; //找到尾
LNode* tailprev = tail->prev; //找到尾的前驱
tailprev->next = phead; //尾前驱节点的后继指针指向头
phead->prev = tail->prev; //头的前驱指针指向尾的前驱
free(tail);
tail = NULL;
}
//双向循环链表头插:
void LPushFront(LNode* phead, LDataType x)
{
if (phead == NULL)
{
return;
}
LNode* newnode = BuyListNode(x);
LNode* next = phead->next; //保存哨兵节点的后继节点
phead->next = newnode; //哨兵点的后继指针指向新节点
newnode->prev = phead; //新节点的前驱指针指向哨兵节点
newnode->next = next; //新节点的后继指针指向哨兵节点的原后继节点
next->prev = newnode; //哨兵节点的原后继节点前驱指针指向新节点
}
//双向循环链表头删:
void LPopFront(LNode* phead)
{
if ((phead == NULL) || (phead->next == phead)) //排除为空的情况
{
return;
}
LNode* next = phead->next; //保存头节点,便于释放
LNode* nextNext = next->next; //保存头节点的后继节点
phead->next = nextNext;
nextNext->prev = phead;
free(next);
next = NULL;
}
//双向循环链表查找:
LNode* LFind(LNode* phead, LDataType x)
{
if (phead == NULL)
{
return;
}
LNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
//双向循环链表给定节点前插:
void LInsert(LNode* pos, LDataType x)
{
if (pos == NULL)
{
return;
}
LNode* posPrev = pos->prev;
LNode* newnode = BuyListNode(x);
posPrev->next = newnode;
newnode->prev = posPrev;
newnode->next = pos;
pos->prev = newnode;
}
//双向循环链表给定节点前插:
void LInsertFront(LNode* pos, LDataType x)
{
if (pos == NULL)
{
return;
}
LNode* posPrev = pos->prev;
LNode* newnode = BuyListNode(x);
posPrev->next = newnode;
newnode->prev = posPrev;
newnode->next = pos;
pos->prev = newnode;
}
//双向循环链表给定节点后插:
void LInsertBack(LNode* pos, LDataType x)
{
if (pos == NULL)
{
return;
}
LNode* posPrev = pos->next;
LNode* newnode = BuyListNode(x);
posPrev->prev = newnode;
newnode->next = posPrev;
newnode->prev = pos;
pos->next = newnode;
}
//双向循环链表给定节点删除:
void LErase(LNode* pos)
{
if (pos == NULL)
{
return;
}
LNode* posPrev = pos->prev;
LNode* posNext = pos->next;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
pos = NULL;
}
//双向循环链表销毁:
void LDestroy(LNode* phead)
{
if(phead==NULL)
{
return;
}
LNode* cur = phead->next;
while (cur != phead);
{
LNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
3.test.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
void LTest()
{
LNode* plist = LInit();
//双向循环链表尾插:
LPushBack(plist, 1);
LPushBack(plist, 2);
LPushBack(plist, 3);
LPushBack(plist, 4);
LPrint(plist); //打印链表
LNode* ret = LFind(plist, 3);
LErase(ret);
LPrint(plist); //打印链表
}
int main()
{
LTest();
return 0;
}
//双向循环链表尾插与尾删:
//LNode* plist = LInit();
双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
//LPushBack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist); //打印链表
双向循环链表尾删:
//LPopBack(plist);
//LPopBack(plist);
//LPopBack(plist);
//LPrint(plist); //打印链表
//双向循环链表头插与头删:
//LNode* plist = LInit();
//双向循环链表头插:
//LPushFront(plist, 1);
//LPushFront(plist, 2);
//LPushFront(plist, 3);
//LPushFront(plist, 4);
//LPrint(plist); //打印链表
双向循环链表头删:
//LPopFront(plist);
//LPopFront(plist);
//LPopFront(plist);
//LPrint(plist); //打印链表
//双向循环链表查找:
//LNode* plist = LInit();
//双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
//LPushBack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist); //打印链表
//LNode* ret = LFind(plist, 3);
//双向循环链表给定位置前插:
//LNode* plist = LInit();
双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
//LPushBack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist); //打印链表
//LNode* ret = LFind(plist, 3);
//LInsertFront(ret, 5);
//LPrint(plist); //打印链表
//双向循环链表给定位置后插:
//LNode* plist = LInit();
双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
![img](https://img-blog.csdnimg.cn/img_convert/ae8bcf9a84a4a53e21b9b05f455c91ec.png)
![img](https://img-blog.csdnimg.cn/img_convert/c66e5bd26e4cdcdfca7b843d51c91615.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
1);
//LPushBack(plist, 2);
//LPushBack(plist, 3);
//LPushBack(plist, 4);
//LPrint(plist); //打印链表
//LNode* ret = LFind(plist, 3);
//LInsertFront(ret, 5);
//LPrint(plist); //打印链表
//双向循环链表给定位置后插:
//LNode* plist = LInit();
双向循环链表尾插:
//LPushBack(plist, 1);
//LPushBack(plist, 2);
[外链图片转存中...(img-VAwK5TwJ-1715716268435)]
[外链图片转存中...(img-ESj69k14-1715716268435)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**