一.带头双向链表的概念
带头双向循环链表(Doubly Circular Linked List with a Head)是一种链表数据结构,它具有以下特点:
头节点(哨兵位):带头双向循环链表包含一个头节点,它位于链表的起始位置,并且不存储实际数据。头节点的前驱指针指向尾节点,头节点的后继指针指向第一个实际数据节点。
循环连接:尾节点的后继指针指向头节点,而头节点的前驱指针指向尾节点,将链表形成一个循环连接的闭环。这样可以使链表在遍历时可以无限循环,方便实现循环操作。
双向连接:每个节点都有一个前驱指针和一个后继指针,使得节点可以向前和向后遍历。前驱指针指向前一个节点,后继指针指向后一个节点。
总结:带头双向循环链表可以支持在链表的任意位置进行插入和删除操作,并且可以实现正向和反向的循环遍历。通过循环连接的特性,链表可以在连续的循环中遍历所有节点,使得链表的操作更加灵活和高效。
二.带头双向循环链表的实现
1.实现框架
通过将int定义为LTDataType,可以在代码中使用LTDataType作为数据类型,而不是直接使用int。这样做的好处有以下几点:
- 可读性:使用LTDataType作为数据类型可以使代码更具可读性。LTDataType作为一个自定义的数据类型名称,可以更好地表达代码中数据的含义和用途,提高代码的可理解性。
- 可维护性:将int定义为LTDataType可以方便地在代码中统一修改数据类型。如果将来需要将数据类型更改为其他类型,只需修改typedef语句中的定义,而不需要在整个代码中逐个修改具体的数据类型,减少了修改的工作量和出错的可能性。
- 灵活性:通过使用LTDataType,可以在代码中轻松更改数据类型,而不会对代码的其他部分产生影响。这种抽象化的方式可以使代码更具通用性,便于在不同的场景中重用。
2.动态内存开辟申请
此函数是关于一个结点动态申请的实现,包含两个指针域,一个数据域。如果分配成功,它会返回指向该内存块起始位置的指针。你可以使用这个指针来访问和操作所分配的内存。如果分配失败,malloc会返回NULL指针,表示内存分配未成功。
3.链表的初始化
链表的初始化就是要创建哨兵位的头节点,此头节点不存储有效数据,并且因一开始不知道指向谁,所以根据双向链表循环的特性,就让该结点的两个指针自己指向自己。
4.链表打印
打印链表就是,遍历链表的每一个结点的数据域,开始时用assert断言传过来的结点地址是否为NULL。接着cur用phead->next赋值的原因是,phead传过来的是哨兵位的头节点,它的下一位才是链表真正的头节点(有数据域),接着遍历链表,当cur指针回到哨兵位时,遍历结束。
5. 释放链表所申请的动态内存空间
在动态内存使用完后,需要把这部分内存的使用权限还给操作系统,否则会造成内存泄漏。将头节点的下一个节点赋值给cur,让cur遍历链表一遍,当cur遍历完成到phead头节点的时候,释放结束,退出循环。
6.判断链表是否为空。
在上面的释放动态内存函数中用到了另外一个函数LTEmpty,这个函数的作用是用来判断链表是否只有哨兵位,空链表的话是不能进行释放的。为此,引用了布尔值,如果为空则返回true,不空则返回false。
7.链表尾部插入节点
首先创建一个指针用于存储最后一个节点,防止尾插时找不到,之后将链表与新节点链接起来就可以了。
测试用例:
尾插几个数据之后,打印出来,最后将内存返回个操作系统,释放哨兵位,防止内存泄露。
这是测试打印出来的数据。
8.链表头部插入节点
首先创建一个指针用于存储第一个节点,防止尾插时找不到,之后将链表与新节点链接起来就可以了。
测试用例:
插入几个数据之后,打印出来,最后将内存返回个操作系统,释放哨兵位,防止内存泄露。
测试数据:
9.链表尾部删除节点
具体怎么实现的都注释在代码边上了,就直接开始测试用例看一下有没有问题。
为了方便对比,每次删除一个节点后都有打印一次链表在屏幕上。
可以看出这个代码是成功的了,将尾部的三个数据都成功的删除了。
那么再来看一下assert函数能不能起作用,这边故意多删除几个,看会不会报错。
这里也是能看见是报错了的,并且能够直接看出来是在list.c文件的第75行出的问题,如果再看一次实现函数的代码的话,确实就能看见是75行的assert报的错误。
10.链表头部删除节点
函数实现:
测试用例:
可以看出,头删也是成功了,因为assert函数的实现是一样的,这边就不过多测试了。
11.查找和修改节点
函数实现:
函数的测试用例:
可以看到这个查找和修改的功能也是成功了。
12.在链表pos之前插入节点
函数实现:
测试用例:
数字9也是成功的插入到了3的前面,那么这个函数也是成功的实现了。
而成功的实现了这个函数,我们就可以将这个函数复用到头插和尾插函数当中。
函数复用实现:
- 头插:
- 尾插:
13.在链表pos处删除该节点
函数实现:
测试用例:
可以看到结果也是与我们预期的相符合,这个函数也是成功了。
同样这个函数也是可以复用到头删和尾删中的。
-
头删:
-
尾删:
test.c
那么接下来就把所有的代码放在这里。
test.c是函数主题的框架,用来执行函数的功能。
list.c
list.c是每个功能函数具体实现的代码
文章来源:https://www.toymoban.com/news/detail-860595.html
list.h
list.h是声明头文件和函数指针以及定义结构体的部分。
本篇完毕,如有错误,欢迎大佬指正!文章来源地址https://www.toymoban.com/news/detail-860595.html
到了这里,关于【数据结构】带头双向循环链表(小白作品,如果有误,请大佬指点)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!