0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

双向循环链表函数是什么?如何去实现它?

Android编程精选 来源:编程学习总站 作者:写代码的牛顿 2021-06-17 12:50 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

1、双向循环链表结点定义和函数声明

双向循环链表结点内部有2个指针prev和next分别指向前后的结点,结点定义代码如下:

typedefstructdlist{ intdata; structdlist*next; structdlist*prev; }dlist_t;

现在我们声明一些双向循环链表操作函数,代码如下:

externdlist_t *new_dlist_node(intdata); //新建一个结点 externdlist_t *dlist_add(dlist_t*list, intdata); //插入一个结点 externdlist_t *dlist_delete(dlist_t*list, intindex); //删除索引对应的结点(索引从0开始) externdlist_t *dlist_index_of(dlist_t*list, intindex); //查找索引对应的结点 externvoiddlist_destroy(dlist_t*list); //销毁双向循环链表 externvoiddlist_print(dlist_t*list); //打印链表数据

可以看到很多函数都会返回一个dlist_t类型的指针,其实这是头结点。很多时候为了书写方便我们会采用typedef定义自己的数据类型,结点定义里为什么next和prev指针可以那样写,参考我上一篇文章,后面会大量这样使用。

2、双向循环链表函数实现

为了更方便释放一个结点内存,我们定义了一个文件作用域静态函数free_dlist_node。

voidfree_dlist_node(dlist_t*node){ if(node == NULL){ return; } node->next = NULL; node->prev = NULL; free(node); node = NULL; }

该函数只负责释放内存的操作。

新建链表结点

dlist_t*new_dlist_node(intdata){ dlist_t*node = (dlist_t*)malloc(sizeof(dlist_t)); if(node == NULL){ returnNULL; } node->data = data; node->next = node; node->prev = node; returnnode; }

这里我们主要注意一点就是新建立的结点next和prev指针会初始化为指向自身,事实上双向循环链表头结点必须这样初始化才能更好的利用双向循环链表特性执行插入、删除和查询等操作。

插入结点

dlist_t *dlist_add(dlist_t *list, int data){ if(list== NULL){ returnNULL; } //新建一个结点 dlist_t *node = new_dlist_node(data); if(node == NULL){ returnlist; } //将结点插入双向循环链表 list->prev->next = node; //最后的结点next指针指向node node->next = list; //node的next指针指向头结点 node->prev = list->prev; //node的prev指针指向原尾结点 list->prev = node; //头结点的prev指针指向新尾结点 returnlist; }

这里list是传入的头结点,我们采用尾插法插入一个结点,最后返回头结点。这里需要注意:每次插入结点都要更新头结点的prev指针。

删除指定位置的结点

dlist_t *dlist_delete(dlist_t *list, int index){ if(list== NULL|| index < 0){         return list;     }     dlist_t *head = list;     int list_index = 0;     //删除链表头结点     if(index == 0){         //链表只有一个结点         if(head == list->next){ free_dlist_node(list); list= NULL; returnNULL; }else{ //链表有大于1个结点 head = head->next; //头结点往后移一个 head->prev = list->prev; //头结点的prev指向尾部结点 list->prev->next = head; //尾部结点的next指针指向头结点 free_dlist_node(list); //释放结点内存 returnhead; } } list= list->next; list_index++; //查询目标结点,通过检查当前结点是否是头结点判断是否已经把双线循环链表遍历了一遍 while(list_index < index && head != list){         list = list->next; list_index++; } //没有找到即将删除的结点 if(head == list){ returnhead; } //找到即将删除的结点 else{ list->prev->next = list->next; //目标结点的上一个结点的next指针指向目标结点的下一个结点 list->next->prev = list->prev; //目标结点的下一个结点的prev指针指向目标结点的上一个结点 free_dlist_node(list); //释放结点内存 returnhead; //返回头结点 } }

删除结点一定要注意:

判断被删除的结点是否是头结点

指定的位置是否超出了链表的长度。

查找指定位置的结点

dlist_t*dlist_index_of(dlist_t*list, intindex){ if(list== NULL|| index < 0){         return NULL;     }     //如果想要获取头结点,则直接返回头结点     if(index == 0){         return list;     }     dlist_t *head = list;     list = list->next; intlist_index = 1; //遍历链表查找指定的索引,通过检查当前结点是否等于头结点判断是否已遍历完毕 while(list_index < index && list != head){         list_index++;         list = list->next; } //没有找到索引对应的结点 if(list== head){ returnNULL; } //找到了索引对应的结点 returnlist; }

查找指定位置结点要注意几个地方:

是否是头结点

是否超出了链表长度

销毁链表

void dlist_destroy(dlist_t *list){ if(list== NULL){ return; } //如果只有一个结点 if(list->next == list){ free_dlist_node(list); return; } dlist_t *temp = list; list= list->next; while(list!= temp){ list= list->next; //遍历下一个结点 temp->prev->next = list; list->prev = temp->prev; temp->next = NULL; temp->prev = NULL; free(temp); temp = list; } free_dlist_node(list); }

打印链表数据

voiddlist_print(dlist_t*list){ if(list== NULL){ return; } dlist_t*head = list; printf("%d, ", list->data); list= list->next; while(list!= head){ printf("%d, ", list->data); list= list->next; } printf(" "); }

最后我们写个小程序验证一下双向循环链表函数实现是否正确。

#include #include"dlist.h" intmain(){ dlist_t*list= new_dlist_node(2); inti = 0; for(i = 0; i < 7; i++){         list = dlist_add(list, 3 + i);     }     printf("打印未处理过的完整链表 ");     dlist_print(list);     list = dlist_delete(list, 0); //删除第一个结点     list = dlist_delete(list, 3); //删除第四个结点     printf("打印删除2个结点后的链表 ");     dlist_print(list);     dlist_t *node = dlist_index_of(list, 2);     printf("第3个结点是:%d ", node->data); printf("销毁链表 "); dlist_destroy(list); list= NULL; return0; }

编译运行输出:

打印未处理过的完整链表 2, 3, 4, 5, 6, 7, 8, 9, 打印删除2个结点后的链表 3, 4, 5, 7, 8, 9, 第3个结点是:5 销毁链表

验证完全正确。

责任编辑:lq6

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4406

    浏览量

    66815
  • 链表
    +关注

    关注

    0

    文章

    80

    浏览量

    10999

原文标题:数据结构与算法篇-双向循环链表

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    控制流和函数调用的精细调整

    特性,避免不必要的计算。 函数调用涉及开销,因为需要保存当前执行环境并跳转到新的执行环境。减少函数调用,尤其是在频繁执行的循环中,可以显著提高性能。 对于简单且频繁调用的
    发表于 11-14 06:32

    rt_object_get_information获取到的链表为空怎么解决?

    rtt启动过程,在初始化堆的时候,进入rt_object_init,调用rt_object_get_information获取到的链表为空,导致系统起不来。
    发表于 10-11 11:44

    人工智能行业如何使用for循环语句进行循环

    : 支持range()函数生成数字序列 可结合else语句使用 Java中的for循环: 传统结构:for(初始化; 条件; 增量) 增强for循环:for(类型 变量 : 集合) 主要用于数组和集合
    的头像 发表于 09-10 12:55 391次阅读

    如何实现高效双向电能变换

    随着电动汽车、家庭和工商业储能产品快速普及,双向电能变换系统的热度也在不断攀升。作为电网与电池的功率桥梁,双向电能变换系统基于一套硬件电路就能控制电池充放电,实现能量双向流动,相比传统
    的头像 发表于 07-23 11:40 1251次阅读

    什么是光伏双向电表?双向电表有哪些应用?

    电能的双向流动轨迹。在用户侧并网运行模式下,不仅计量用户从公共电网获取的用电量(正向有功电能),同时精准统计光伏系统向电网回馈的发电量(逆向有功电能),实现能源流量的全维度监控。 技术支持 安科瑞 程瑜 187 0211 2087 双向
    的头像 发表于 05-12 09:42 1613次阅读
    什么是光伏<b class='flag-5'>双向</b>电表?<b class='flag-5'>双向</b>电表有哪些应用?

    函数指针的六个常见应用场景

    函数指针在嵌入式开发中有着广泛的应用,让代码更加灵活,减少冗余,提高可扩展性。很多时候,我们需要根据不同的情况动态调用不同的函数,而函数指针正是
    的头像 发表于 04-07 11:58 1119次阅读
    <b class='flag-5'>函数</b>指针的六个常见应用场景

    给uint32_t数组填充整型值,除使用循环赋值外有没有c库函数可以实现

    给uint32_t数组填充整型值,除使用循环赋值外有没有c库函数可以实现
    发表于 03-07 17:05

    技术干货驿站 ▏深入理解C语言:嵌套循环循环控制的底层原理

    大家好!在上一节中,我们学习了C语言中的基本循环语句,如for、while和do...while循环。今天,我们将进一步探讨嵌套循环循环控制,这些技巧可以帮助我们
    的头像 发表于 02-21 18:26 1037次阅读
    技术干货驿站  ▏深入理解C语言:嵌套<b class='flag-5'>循环</b>与<b class='flag-5'>循环</b>控制的底层原理

    火语言如何循环读取表格

    名为id的指定行列数据、dt.Rows.Count取表格的总行数来实现表格的循环读取。 配置 结果 审核编辑 黄宇
    的头像 发表于 02-07 15:11 512次阅读
    火语言如何<b class='flag-5'>循环</b>读取表格

    SN74CBTD3306能用于2.5V-3.3V双向电平转换吗?

    我之前问过Ti关于3.3V-2.5V双向电平转换芯片的问题,我看到技术文档中有提到 FET Switch这种解决方法,但我查这个芯片的文档,发现只有阈值电压和oe1 oe2两个使能信号,没有发现
    发表于 02-05 08:14

    可靠性温度循环试验至少需要几个循环

    温度循环作为自然环境的模拟,可以考核产品在不同环境条件下的适应能力,常用于产品在开发阶段的型式试验、元器件的筛选试验。一、温度循环测试介绍温度循环试验,也称为热循环试验、高低温
    的头像 发表于 01-23 15:26 987次阅读
    可靠性温度<b class='flag-5'>循环</b>试验至少需要几个<b class='flag-5'>循环</b>?

    stdio.h实现了printf函数?

    我们平时包含的 stdio.h 头文件,里面是不是实现了 printf 函数? 为什么会有这个疑问?因为每次使用 printf,就得包含 stdio.h ,这就导致很多同学误以为,stdio.h
    的头像 发表于 12-18 10:28 866次阅读

    解析$nextTick魔力,为啥大家都爱

    作者:京东保险 卓雅倩 1.为什么需要使用$nextTick? 首先我们来看看官方对于$nextTick的定义: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取
    的头像 发表于 12-17 10:02 825次阅读
    解析$nextTick魔力,为啥大家都爱<b class='flag-5'>它</b>?

    储能系统的核心——双向逆变器

    电子发烧友网报道(文/黄山明)所谓双向逆变器,就是一种能够实现直流电(DC)与交流电(AC)之间双向转换的电力电子设备。的主要作用是在储能系统中
    的头像 发表于 12-17 00:08 6323次阅读
    储能系统的核心——<b class='flag-5'>双向</b>逆变器

    深入理解C语言:循环语句的应用与优化技巧

    在程序设计中,我们常常需要重复执行某一段代码。为了提高效率和简化代码,循环语句应运而生。C语言作为一门经典的编程语言,提供了多种循环控制结构,帮助程序员高效地实现重复操作。掌握循环语句
    的头像 发表于 12-07 01:11 1059次阅读
    深入理解C语言:<b class='flag-5'>循环</b>语句的应用与优化技巧