当前位置:网站首页>双向循环带头链表
双向循环带头链表
2022-08-05 07:07:00 【Science52】
上次我们学习了单链表,但是在自己实现的时候我们不难发现单链表其实不适合存储数据,对于头删头插它的效率可以,但是对于其他的功能来说,这显得太复杂。每次都要遍历一次,而每遍历一次都是要消耗时间的,所以单链表的效率不高。
下面的双向循环带头链表就解决了这个问题:
还是像之前的单链表一样,我们先看看接口:
ListNode* LTInit();
ListNode* BuyNode(LTDataType x);
void LTPrint(ListNode* phead);
void LTPushFront(ListNode* phead, LTDataType x);
void LTPushBack(ListNode* phead, LTDataType x);
void LTPopFront(ListNode* phead);
void LTPopBack(ListNode* phead);
ListNode* ListFind(ListNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
// 双向链表销毁
void ListDestory(ListNode* phead);
//链表的数据个数
size_t ListSize(ListNode* phead);
//判断链表是否为空
bool IsListEmpty(ListNode* phead);
那么首先我们要创建结构体来表示每一个节点:
这样创建之后初始化就没有单链表那么简单了,这是我们可以用一个函数去实现初始化:
ListNode* LTInit()//链表初始化
{
ListNode* guard = (ListNode*)malloc(sizeof(ListNode));//创建哨兵位的头节点
if (guard == NULL)
{
perror("malloc fail");
exit(-1);
}
guard->next = guard;
guard->prev = guard;
return guard;
}
根据我们之前学单链表我们可以知道头插头删是可以不需要单独写的,这次就主要把任意位置的插入和删除写出来直接调用函数就可以解决头插头删的问题了:
在这之前我们要找到位置并创建节点:
ListNode* BuyNode(LTDataType x)//创建节点
{
ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
if (newnode == NULL)
{
perror("malloc fail");//防止创建失败
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
ListNode* ListFind(ListNode* phead, LTDataType x)//查找数据,查找之后可以通过对指针的解引用来修改数据
{
assert(phead);
ListNode* cur = phead->next;//哨兵位的头节点不存储数据
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
对于查和改其实是一回事,只要查到了改就很简单了:
下面就演示了修改的操作:
找到数据了,之后就可以在任意位置插入删除了,因为双向循环带头链表是很好找前一个的,所以我们在插入数据时一般都是插入在当前位置的前一个:
void ListInsert(ListNode* pos, LTDataType x)//任意位置之前的插入
{
assert(pos);
ListNode* prev = pos->prev;//记录pos之前的位置
ListNode* newnode = BuyNode(x);
//建立链接关系
newnode->prev = prev;
newnode->next = pos;
prev->next = newnode;
pos->prev = newnode;
}
删除也是很简单:
void ListErase(ListNode* pos)//任意位置的删除
{
assert(pos);
assert(!IsListEmpty(pos));
ListNode* prev = pos->prev;//记录前一个位置
//建立前一个和后一个的链接
prev->next = pos->next;
pos->next->prev = prev;
free(pos);//删除节点
pos = NULL;//这里置空无效,因为这里是零时变量,应该由使用者置空
}
在完成了这几个函数之后头插头删也变得异常简单:
void LTPushFront(ListNode* phead, LTDataType x)//头插
{
assert(phead);
ListInsert(phead->next, x);//因为这个是在pos位置之前插入,所以要传第一个
}
void LTPushBack(ListNode* phead, LTDataType x)//尾插
{
assert(phead);
ListInsert(phead, x);//头的前面就是尾
}
void LTPopFront(ListNode* phead)//头删
{
assert(phead);
assert(!IsListEmpty(phead));
ListErase(phead->next);//不能删除带哨兵位的头节点
}
void LTPopBack(ListNode* phead)//尾删
{
assert(phead);
assert(!IsListEmpty(phead));
ListErase(phead->prev);//头的前一个就是尾就是pos的位置
}
这里删除的时候用一个函数去判断链表是否为空:
bool IsListEmpty(ListNode* phead)//判断链表是否为空
{
assert(phead);
return phead->next == phead;//如果为相等的话,那么就为空,返回真
}
在数据都插入删除后我们可以通过实现打印函数,更直观的观察:
void LTPrint(ListNode* phead)//打印数据
{
assert(phead);
ListNode* cur = phead->next;
printf("phead<=>");
while (cur != phead)//因为是循环链表,所以最后的条件应该是不等于头节点结束
{
printf("%d<=>",cur->data);
cur = cur->next;
}
printf("\n");
}
如果我们想知道存储了多少数据,我们也可以通过遍历一次来求出数据的个数:
size_t ListSize(ListNode* phead)//计算链表数据的个数
{
assert(phead);
ListNode* cur = phead->next;//头节点不算数据
size_t n = 0;
while (cur != phead)
{
++n;
cur = cur->next;
}
return n;
}
最后我们使用完后要记得销毁链表,不然会导致内存泄漏:
void ListDestory(ListNode* phead)//链表的销毁
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
ListNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;//这里不起作用,要销毁的那个人自己置空,这里保证了接口参数的一致性
}
在实现双向循环链表的时候能明显感觉到其存储数据的高效,很多功能十分容易就能实现
在对比顺序表以及链表的时候,我们不能说链表就一定优胜顺序表,顺序表有着一个非常大的优势,那就是随机访问,这个优势就是顺序表不能被链表取代的原因。
顺序表的缓存利用率更高:
边栏推荐
- FPGA parsing B code----serial 4
- UDP group (multi)cast
- 基于KECA-IGWO-KELM的间歇过程故障诊断方法
- 二叉搜索树问题
- AI + video technology helps to ensure campus security, how to build a campus intelligent security platform?
- After the firewall iptable rule is enabled, the system network becomes slow
- 【instancetype类型 Objective-C】
- Vulnhub靶机:HA_ NARAK
- 《基于R语言的自动数据收集》--第3章 XML和JSON
- In the anaconda Promat interface, import torch is passed, and the error is reported in the jupyter notebook (only provide ideas and understanding!)
猜你喜欢
Hash 这些知识你也应该知道
Put Cloudflare on the website (take Tencent Cloud as an example)
Flink学习11:flink程序并行度
Shiny04---Application of DT and progress bar in shiny
MySQL:order by排序查询,group by分组查询
在anaconda Promat界面import torch通过,在jupyter notebook中报错的问题(仅提供思路理解!)
binary search tree problem
一天学会从抓包到接口测试,通过智慧物业项目深度解析
TRACE32——List源代码查看
Advanced Redis
随机推荐
Takeda Fiscal 2022 First Quarter Results Strong; On Track to Achieve Full-Year Management Guidance
After the firewall iptable rule is enabled, the system network becomes slow
字符串提取 中文、英文、数字
算法拾遗十五补链表相关面试题
C# FileSystemWatcher
Mysql为什么 建立数据库失败
文本特征化方法总结
Rapid Medical's Ultra-Small and Only Adjustable Thromb Retriever Receives FDA Clearance
(4) Rotating object detection data roLabelImg to DOTA format
不太会讲爱,其实已经偷偷幸福很久啦----我们的故事
Flink学习11:flink程序并行度
2022 crane driver (limited bridge crane) exam question bank and simulation test
Shiny04---Application of DT and progress bar in shiny
DeFi 前景展望:概览主流 DeFi 协议二季度进展
MySQL: JDBC programming
An IP conflict is reported after installing the software on a dedicated computer terminal
AI + video technology helps to ensure campus security, how to build a campus intelligent security platform?
UDP group (multi)cast
MobileNetV2架构解析
Technical Analysis Mode (8) Double Top and Bottom