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

    文章

    3868

    浏览量

    61308
  • 链表
    +关注

    关注

    0

    文章

    80

    浏览量

    10464

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

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

收藏 人收藏

    评论

    相关推荐

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

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

    数组和链表有何区别

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

    数据结构:单链表的排序

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

    python怎么设置循环次数

    在Python中,可以使用循环语句来重复执行一段代码多次。要设置循环次数,可以使用循环的计数器来控制循环的执行次数。以下是几种常用的设置循环
    的头像 发表于 11-23 15:50 1912次阅读

    LinkedBlockingQueue基于单向链表实现

    的 LinkedBlockingQueue。它的底层基于单向链表实现。 先看一看它的 Node 内部类和主要属性、构造函数。 Node static class Node E > { E item; Node next; Nod
    的头像 发表于 10-13 11:41 274次阅读
    LinkedBlockingQueue基于单向<b class='flag-5'>链表</b>的<b class='flag-5'>实现</b>

    循环链表双向链表(2)#数据结构

    数据函数二叉树
    未来加油dz
    发布于 :2023年09月05日 09:52:24

    循环链表双向链表(1)#数据结构

    数据函数二叉树
    未来加油dz
    发布于 :2023年09月05日 09:51:25

    C语言链表知识点(2)

    C语言链表知识点(2)
    发表于 08-22 10:38 177次阅读
    C语言<b class='flag-5'>链表</b>知识点(2)

    如何判断链表是否有环

    如何判断链表是否有环?
    发表于 08-10 17:07 471次阅读
    如何判断<b class='flag-5'>链表</b>是否有环

    如何创建单链表

    实际中经常使用的一般为带头双向循环链表。 单链表1 # include # include typedef struct node { int data; //"数据域" 保存数据元素
    的头像 发表于 07-27 16:05 928次阅读
    如何创建单<b class='flag-5'>链表</b>

    双向循环链表创建代码

    双向循环链表demo #include #include typedef struct node { int data; //"数据域" 保存数据元素 struct node * next
    的头像 发表于 07-27 11:26 709次阅读
    <b class='flag-5'>双向</b><b class='flag-5'>循环</b><b class='flag-5'>链表</b>创建代码

    链表和双链表的区别在哪里

    。 上面的三幅图对于理解链表的插入、删除很重要,看代码的时候要对着看。 实际中经常使用的一般为带头双向循环链表,下面是一个双向
    的头像 发表于 07-27 11:20 1103次阅读
    单<b class='flag-5'>链表</b>和双<b class='flag-5'>链表</b>的区别在哪里

    链表数据结构基本概念

    链表基本概念 头指针: 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。 头指针具有标识作用,所以常用头指针冠以链表的名字。 无论
    的头像 发表于 07-27 11:14 613次阅读
    <b class='flag-5'>链表</b>数据结构基本概念

    C语言算法题:反转一个单向链表

    链表是编程学习的一个难点。其实,在C语言编程以及单片机裸机开发中,链表运用并不多。但是如果想提升嵌入式技能水平或收入水平,可以考虑深入嵌入式系统层面(如参与操作系统设计、深入学习新的操作系统等),此时,链表技术至关重要。
    发表于 06-21 11:07 389次阅读
    C语言算法题:反转一个单向<b class='flag-5'>链表</b>

    C语言实现链表-增删改查

    链表是由一连串节点组成的数据结构,每个节点包含一个数据值和一个指向下一个节点的指针。链表可以在头部和尾部插入和删除节点,因此可以在任何地方插入和删除节点,从而使其变得灵活和易于实现
    的头像 发表于 05-25 15:05 939次阅读
    C语言<b class='flag-5'>实现</b>单<b class='flag-5'>链表</b>-增删改查