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

    文章

    4422

    浏览量

    67851
  • 链表
    +关注

    关注

    0

    文章

    80

    浏览量

    11086

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

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

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

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    什么是热循环试验?有哪些设备?怎么做热循环试验?

    循环试验,也称为温度循环试验、高低温循环试验,是一种在常压下进行试件温度循环的可靠性试验,用于评估材料、组件或产品在温度变化条件下的耐久性和可靠性。通过模拟实际使用过程中可能遇到的温
    的头像 发表于 04-22 15:20 70次阅读
    什么是热<b class='flag-5'>循环</b>试验?有哪些设备?怎么做热<b class='flag-5'>循环</b>试验?

    【快速温变循环】快速温变循环试验箱的“循环”之道:宏展科技如何定义“一个循环

    在军工、航天、汽车电子等高端制造领域,快速温变循环试验是验证产品可靠性的核心手段。然而,“一个循环”到底如何定义?是简单的升降温,还是对速率、驻留时间、温变曲线精度、循环重复性的严苛约束?广东宏展
    的头像 发表于 04-16 09:38 275次阅读
    【快速温变<b class='flag-5'>循环</b>】快速温变<b class='flag-5'>循环</b>试验箱的“<b class='flag-5'>循环</b>”之道:宏展科技如何定义“一个<b class='flag-5'>循环</b>”

    基于 SiC MOSFET 的高性能双向 DAB 变换器全负载范围 ZVS 实现与优化

    基于 SiC MOSFET 的高性能双向 DAB 变换器全负载范围 ZVS 实现与优化指南 1. 绪论 在现代分布式能源架构、大规模储能系统(ESS)、电动汽车(EV)超快速充电网络以及车网互动
    的头像 发表于 04-07 11:33 595次阅读
    基于 SiC MOSFET 的高性能<b class='flag-5'>双向</b> DAB 变换器全负载范围 ZVS <b class='flag-5'>实现</b>与优化

    嵌入式春招笔试高频算法题(附解题思路)

    循环,体现代码效率,笔试会加分。 二、高频算法题2:链表反转(中大厂高频) 链表是嵌入式开发的核心数据结构,链表反转是春招中大厂笔试的高频算法题,重点考察
    发表于 03-18 10:08

    功率循环基础篇(二) —— 功率循环寿命曲线解读

    功率循环寿命曲线是评估功率半导体器件(如  IGBT 模块)在温度交变应力下长期可靠性的核心工具。该曲线通常以 结温波动幅度 ΔTj 为横坐标,以器件达到指定失效判据前所经历的 循环次数 Nf 为
    的头像 发表于 03-02 11:55 334次阅读
    功率<b class='flag-5'>循环</b>基础篇(二) —— 功率<b class='flag-5'>循环</b>寿命曲线解读

    CS5801搭配AS721芯片实现HDMI转DP双向互转方案

    CS5801与AS721芯片组合实现HDMI与DP双向互转。CS5801支持HDMI2.0b转DP1.4a,提供4K@60Hz传输;AS721作为低功耗交换机芯片实现双向信号切换。方案
    的头像 发表于 01-21 10:20 466次阅读
    CS5801搭配AS721芯片<b class='flag-5'>实现</b>HDMI转DP<b class='flag-5'>双向</b>互转方案

    如何在Zephyr RTOS中实现延时和计时函数

    多种延时与计时实现方案,满足不同应用场景的需求。那么,大家平时都是怎么在MCU程序中实现计时函数实现延时的呢?
    的头像 发表于 12-26 10:32 6120次阅读
    如何在Zephyr RTOS中<b class='flag-5'>实现</b>延时和计时<b class='flag-5'>函数</b>

    内存拷贝函数 memcpy原理及实现

    内存拷贝函数memcpymemcpy是memory copy的缩写,意为内存复制,在写C语言程序的时候,我们常常会用到的函原型如下:void *memcpy(void *dest, const
    发表于 12-26 08:03

    无数据域双向链表的代码

    ); return 0; } 在这个示例中,我们定义了一个包含指向前一个节点和后一个节点的结构体 Node,以及一个包含整数数据和 Node 结构体的结构体 Data。然后实现了插入和打印链表函数。在打
    发表于 12-11 06:56

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

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

    使用函数实现三相电机正反转控制

    在使用西门子S1200PLC,所使用的软件是博途软件,在这个软件里运用了块的概念。比如我们常见的组织块(OB)、函数块(FB)、数据块(DB)以及函数FC等。今天我们来具体交流一下这个函数块(FB)的具体使用方法。
    的头像 发表于 10-15 14:40 2920次阅读
    使用<b class='flag-5'>函数</b>块<b class='flag-5'>实现</b>三相电机正反转控制

    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 696次阅读

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

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

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

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