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

    文章

    4304

    浏览量

    62426
  • 链表
    +关注

    关注

    0

    文章

    80

    浏览量

    10547

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

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

收藏 人收藏

    评论

    相关推荐

    双向晶闸管交流调压的工作原理

    双向晶闸管(Bidirectional Thyristor,简称Triac)交流调压技术是一种重要的电力电子技术,利用双向晶闸管的特性来实现对交流电压的精确控制。
    的头像 发表于 10-18 17:51 746次阅读

    TS3DV621双向复用器/复用器数据表

    电子发烧友网站提供《TS3DV621双向复用器/复用器数据表.pdf》资料免费下载
    发表于 07-10 09:14 0次下载
    TS3DV621<b class='flag-5'>双向</b>复用器/<b class='flag-5'>去</b>复用器数据表

    DS64BR401带均衡和加重功能的四路双向中继器数据表

    电子发烧友网站提供《DS64BR401带均衡和加重功能的四路双向中继器数据表.pdf》资料免费下载
    发表于 07-05 09:37 0次下载
    DS64BR401带均衡和<b class='flag-5'>去</b>加重功能的四路<b class='flag-5'>双向</b>中继器数据表

    OpenHarmony语言基础类库【@ohos.util.LinkedList (线性容器LinkedList)】

    LinkedList底层通过双向链表实现双向链表的每个节点都包含对前一个元素和后一个元素的引用。当需要查询元素时,可以从头遍历,也可以从尾
    的头像 发表于 05-11 16:16 510次阅读
    OpenHarmony语言基础类库【@ohos.util.LinkedList (线性容器LinkedList)】

    双向储能变流器的工作原理

    双向储能变流器(PCS),又称双向储能逆变器,是储能系统与电网之间实现电能双向流动的核心部件。的主要功能包括控制电池的充电和放电过程,并进
    的头像 发表于 05-06 17:30 1476次阅读

    电流监控如何实现高效双向电流检测

    为避免上述情况发生,设计人员可以采用集成、高速、精确的双向 CSA。设计人员可以选择带有内部低电感分流电阻的集成双向 CSA 构成最紧凑的解决方案,或者选择使用外部分流器的 CSA 实现更灵活的设计和布局。
    发表于 04-11 09:14 381次阅读
    电流监控如何<b class='flag-5'>实现</b>高效<b class='flag-5'>双向</b>电流检测

    关于stm32u575芯片作为usb device和PC实现双向通信的疑问

    平台:STM32U575qii-EV板 模块:USBX,ThreadX 目的:stm32u575芯片作为usb device和PC实现双向通信,device为HID Custom类 现状:当前
    发表于 03-13 06:56

    回调函数(callback)是什么?回调函数实现方法

    回调函数是一种特殊的函数作为参数传递给另一个函数,并在被调用函数执行完毕后被调用。回调函数
    发表于 03-12 11:46 2712次阅读

    数组和链表在内存中的区别 数组和链表的优缺点

    数组和链表在内存中的区别 数组和链表的优缺点  数组和链表是常见的数据结构,用于组织和存储数据。它们在内存中的存储方式以及优缺点方面存在一些显著的差异。本文将详细探讨这些差异以及它们的优缺点。 1.
    的头像 发表于 02-21 11:30 910次阅读

    数组和链表有何区别

    数组和链表的区别,这个问题,不仅面试中经常遇到,考研的同学也得掌握才行。
    的头像 发表于 02-19 15:33 450次阅读
    数组和<b class='flag-5'>链表</b>有何区别

    循环指令loop规定循环次数

    循环指令是计算机编程中非常重要的概念,允许程序重复执行一段代码块,使得程序可以更有效地处理大量数据和重复性任务。在本文中,我们将详尽、详实、细致地介绍循环指令的相关概念、语法和应用场
    的头像 发表于 02-14 16:10 1470次阅读

    数据结构:删除有序链表的重复节点

    给定一个有序单链表(从小到大有序)的头结点head(该结点有值),删除链表中的重复元素,使链表中的所有元素都只出现一次。如当输入 {1,1,2} 时,经删除后,原链表变为 {1,2},
    的头像 发表于 12-05 15:46 863次阅读
    数据结构:删除有序<b class='flag-5'>链表</b>的重复节点

    利用SCR步进指令实现流水灯循环点亮

    在以往介绍西门子smart文章中,提到过流水灯的写法,有用循环位移字[ROL(R)_B]节来实现,有利用定时器(TON)来实现。今天我们利用SCR步进指令来实现流水灯
    的头像 发表于 12-04 09:15 2115次阅读
    利用SCR步进指令<b class='flag-5'>实现</b>流水灯<b class='flag-5'>循环</b>点亮

    数据结构:判断链表回文结构

    给定一个链表,判断该链表是否为回文结构。回文是指该字符串正序逆序完全一致。如当输入链表 {1,2,3,2,1} 时,断定是回文结构,输出True。
    的头像 发表于 12-01 13:26 611次阅读
    数据结构:判断<b class='flag-5'>链表</b>回文结构

    数据结构:单链表的排序

    给定一个单链表的头结点head(该结点有值),长度为n的无序单链表,对其按升序排序后,返回新链表。如当输入链表 {3,1,4,5,2} 时,经升序排列后,原
    的头像 发表于 11-30 13:56 1502次阅读
    数据结构:单<b class='flag-5'>链表</b>的排序