• 【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表)


    =========================================================================

    相关代码gitee自取

    C语言学习日记: 加油努力 (gitee.com)

     =========================================================================

    接上期

    【数据结构初阶】三、 线性表里的链表(无头+单向+非循环链表 -- C语言实现)-CSDN博客

     =========================================================================

                         

     引言 

    通过上期单链表(无头+单向+非循环链表)介绍使用

    我们可以知道顺序表和链表的区别

                  

                  

    顺序表和链表的一些区别:

                  

    • 单链表无头+单向+非循环链表只有一个后继指针next指向下一个结点
      双向链表不仅有后继指针next还有一个前驱指针prev指向上一个结点
                           
    • 上篇单链表只能顺着往后遍历不能倒着往回走
      会造成一些操作很困难回文逆置等操作),
      双向链表顺着往后遍历也能倒着往回遍历

    更多区别图示:

    不同点(方面)顺序表链表
    存储空间上物理上一定连续逻辑连续,但物理不一定连续
    随机访问支持 -- O(1)不支持 -- O(N)
    任意位置插入或者删除可能需要搬移元素效率低 -- O(N)只需修改指针指向
    插入时的容量(空间)动态顺序表空间不够需要扩容没有容量的概念(分成一个个结点)
    应用场景元素高效存储+频繁访问频繁在任意位置插入和删除
    缓存利用率
    注:缓存利用率 参考 存储体系结构 以及 局部原理性

                   

                    

    回顾上期中提到的带头双向循环链表

               

    带头双向循环链表

    简介:

    结构最复杂一般用在单独存储数据

    实际中使用的链表数据结构,都是带头双向循环链表

    另外这个结构虽然结构复杂

    但是使用代码实现以后会发现结构会带来很多优势实现反而简单

    图示:

             

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                 

    1 . 双向链表的实现
    (带头+双向+循环 链表)

    (详细解释在图片的注释中,代码分文件放最后)
                      

    实现具体功能前的准备工作

    • 包含之后会用到的头函数
                                 
    • 创建双向链表数据类型 -- 链表结点中数据域里存储的数据的类型
                   
    • 创建双向链表结点结构体(类型) -- 结点中应有 数据域  指针域
    图示

                

                

    ---------------------------------------------------------------------------------------------

                

    BuyLTNode函数 -- 创建双向循环链表结点

    • 为创建结点开辟动态空间,并检查是否开辟成功
                            
    • 开辟成功后初始化结点数据域指针域
                     
    • 最后返回开辟的空间地址
    图示

                

                

    ---------------------------------------------------------------------------------------------

                

    LTInit函数 -- 带头双向循环链表初始化函数

    • 初始化时先使用BuyLTNode函数创建哨兵位
                  
    • 因为要实现循环
      所以让哨兵位后继指针next前驱指针prev都指向自己
                      
    • 初始化后返回链表哨兵位
    图示

                

                

    ---------------------------------------------------------------------------------------------

                

    LTPrint函数 -- 打印双向链表各结点数据域数据

    • assert断言头指针(哨兵位地址)不为空
                  
    • 创建结点指针cur进行遍历
                      
    • 使用while循环进行遍历打印

    图示

    测试 -- LTPrint函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTPushBack函数 -- 向链表尾部插入一个结点(尾插)

    • assert断言头指针(哨兵位地址)不为空
                  
    • 通过哨兵位配合前驱指针prev获得尾结点地址
                      
    • 调用BuyLTNode函数为尾插操作创建尾插结点newnode

                       

    • 尾插结点原尾部结点连接
                       

    • 尾插结点哨兵位进行连接

    图示

    测试 -- LTPushBack函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTPopBack函数 -- 删除链表尾部结点(尾删)

    • assert断言 头指针(哨兵位地址)不为空双向链表不为空链表
                  
    • 通过哨兵位的前驱指针prev获得尾结点tail
      通过尾结点tail获得倒数第二个结点tailPrev
                      
    • 释放删除尾结点tail

                       

    • 倒数第二个结点tailPrev成为新的尾结点
      为保持循环,把tailPrev哨兵位连接起来

    图示

    测试 -- LTPopBack函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTPushFront函数 -- 向链表头部插入一个结点(头插)

    • assert断言头指针(哨兵位地址)不为空
                  
    • 调用BuyLTNode函数为头插操作创建头插结点newnode
                      
    • 创建一个first指针保存原本第一个结点地址

                       

    • 哨兵位后继指针next指向头插结点newnode
      头插结点newnode前驱指针prev指向哨兵位
                       

    • 头插结点newnode后继指针next指向原本头结点first
      原本头结点first前驱指针prev指向头插结点newnode

    图示

    测试 -- LTPushFront函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTPopFront函数 -- 删除链表头部结点(头删)

    • assert断言 头指针(哨兵位地址)不为空双向链表不为空链表
                  
    • 通过哨兵位后继结点next获得头结点地址
      再通过first结点获得第二个结点
                      
    • 释放头结点first

                       

    • 哨兵位后继结点next指向第二个结点second
      第二个结点的前驱指针prev指向哨兵位

    图示

    测试 -- LTPopFront函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTSize函数 -- 求链表有效结点个数(求链表长度)

    • assert断言头指针(哨兵位地址)不为空
                  
    • 创建变量size存放链表长度
      创建结点指针cur进行遍历
                      
    • 使用while循环遍历链表计算链表长度

                       

    • 最后返回链表长度size

    图示

    测试 -- LTSize函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTFind函数 -- 在双向链表中查找数据域数据为x的结点地址

    • assert断言头指针(哨兵位地址)不为空
                  
    • 创建遍历指针cur保存第一个结点地址
                      
    • 使用while循环进行遍历查找

                       

    • 未找到则返回空指针

    图示

    测试 -- LTFind函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTInsert函数 -- 在pos结点之前插入数据域数据为x的结点

    • assert断言头指针(哨兵位地址)不为空
                  
    • 通过pos结点获得前一个结点posPrev地址
                      
    • 使用BuyLTNode函数为插入结点开辟空间

                       

    • posPrev结点的后继指针next指向newnode
      newnode前驱指针prev指向posPrev
                       

    • newnode后继指针next指向pos
      pos结点前驱指针prev指向newnode

    图示

    测试 -- LTInsert函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTErase函数 -- 删除pos结点

    • assert断言删除位置结点pos不为空
                  
    • 保存删除结点pos的前一个结点posPrev地址
      保存删除结点pos的后一个结点posNext地址
                      
    • 释放掉pos结点

                       

    • 将pos前结点posPrev的后继指针指向posNext
      将pos后结点posNext的前驱指针指向posPrev

    图示

    测试 -- LTErase函数

                

                

    ---------------------------------------------------------------------------------------------

                

    LTDestroy函数 -- 销毁链表

    • assert断言头指针(哨兵位地址)不为空
                  
    • 创建遍历指针cur保存第一个结点地址
                      
    • 使用while循环遍历释放有效结点

                       

    • 最后释放哨兵位

    图示

             

    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                 

    2 . 对应代码

    List.h -- 双向链表头文件

    1. #pragma once
    2. //双向链表头文件:
    3. //包含之后需要用到的头文件:
    4. #include
    5. #include
    6. #include
    7. //定义双向链表数据域数据类型:
    8. typedef int LTDataType;
    9. //创建双向链表结点类型:
    10. typedef struct ListNode
    11. {
    12. //数据域:
    13. LTDataType data;
    14. //双向链表指针域:
    15. //后继指针--指向后一个结点:
    16. struct ListNode* next;
    17. //前驱指针--指向前一个结点:
    18. struct ListNode* prev;
    19. }LTNode; //类型简称LTNode
    20. //函数声明:
    21. //创建链表结点--创建双向循环链表结点
    22. //接收要插入创建结点数据域的数据
    23. //返回创建结点的地址
    24. LTNode* BuyLTNode(LTDataType x);
    25. //双向链表初始化--带头双向循环链表初始化函数
    26. //返回初始化结点的地址
    27. LTNode* LTInit();
    28. //打印链表--打印双向链表各结点数据域数据
    29. //接收链表头指针(phead)
    30. LTNode* LTPrint(LTNode* phead);
    31. //双向链表尾插函数--向链表尾部插入一个结点(尾插):
    32. //接收链表头指针(phead)、要尾插进链表的值(x)
    33. void LTPushBack(LTNode* phead, LTDataType x);
    34. //双向链表尾删函数--删除链表尾部结点(尾删)
    35. //接收链表头指针(phead)
    36. void LTPopBack(LTNode* phead);
    37. //双向链表头插函数--向链表头部插入一个结点(头插):
    38. //接收链表头指针(phead)、要头插进链表的值(x)
    39. void LTPushFront(LTNode* phead, LTDataType x);
    40. //双向链表头删函数--删除链表头部结点(头删)
    41. //接收链表头指针(phead)
    42. void LTPopFront(LTNode* phead);
    43. //求链表结点个数函数--求链表有效结点个数(求链表长度)
    44. //接收链表头指针(phead)
    45. int LTSize(LTNode* phead);
    46. //双向链表查找函数--在双向链表中查找数据域数据为x的结点地址
    47. //接收链表头指针(phead)、要在链表中查找的值(x)
    48. LTNode* LTFind(LTNode* phead, LTDataType x);
    49. //双向链表插入函数--在pos结点之前插入数据域数据为x的结点
    50. //接收插入位置(pos)、要插入链表的值(x)
    51. void LTInsert(LTNode* pos, LTDataType x);
    52. //双向链表删除函数--删除pos结点
    53. //接收要删除结点地址(pos)
    54. void LTErase(LTNode* pos);
    55. //双向链表销毁函数--销毁链表
    56. //接收要销毁链表头系欸但(phead)
    57. void LTDestroy(LTNode* phead);

                

                

    ---------------------------------------------------------------------------------------------

                    

    List.c -- 双向链表函数实现文件

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. //双向链表函数实现文件:
    3. //包含双向链表头文件:
    4. #include "List.h"
    5. //函数实现:
    6. //创建链表结点--创建双向循环链表结点
    7. //接收要插入创建结点数据域的数据
    8. LTNode* BuyLTNode(LTDataType x)
    9. {
    10. //为创建结点开辟动态空间:
    11. LTNode* node = (LTNode*)malloc(sizeof(LTNode));
    12. //检查是否开辟失败:
    13. if (node == NULL)
    14. //返回NULL,开辟失败
    15. {
    16. //打印错误信息:
    17. perror("malloc fail");
    18. //终止程序:
    19. exit(-1);
    20. }
    21. //把x放入结点数据域:
    22. node->data = x;
    23. //设置双向链表指针域:
    24. node->next = NULL;
    25. node->prev = NULL;
    26. //开辟成功则返回开辟空间地址
    27. return node;
    28. }
    29. //链表初始化--带头双向循环链表初始化函数
    30. //接收链表头指针(phead)
    31. LTNode* LTInit()
    32. {
    33. //初始化时先使用BuyLTNode函数创建哨兵位:
    34. LTNode* phead = BuyLTNode(-1); //返回哨兵位指针
    35. //因为要实现循环,
    36. //所以让哨兵位后继指针next和前驱指针prev都指向自己:
    37. phead->next = phead;
    38. phead->prev = phead;
    39. //这样即使链表为空,它也是有头有尾的,即哨兵位phead
    40. //初始化后返回链表哨兵位:
    41. return phead;
    42. }
    43. //打印链表--打印双向链表各结点数据域数据
    44. //接收链表头指针(phead)
    45. LTNode* LTPrint(LTNode* phead)
    46. {
    47. //assert断言头指针(哨兵位地址)不为空:
    48. assert(phead != NULL);
    49. //创建结点指针cur进行遍历:
    50. //指针cur应该从哨兵位(头指针)下一个结点开始
    51. LTNode* cur = phead->next;
    52. printf("phead <==> ");
    53. //因为是循环链表,不是以NULL空指针结尾
    54. //所以应该是当指针cur遍历回到哨兵位就终止遍历:
    55. while (cur != phead)
    56. //如果只用哨兵位,链表为空,
    57. //phead->next还是phead,不会进行打印
    58. {
    59. //打印数据域内容:
    60. printf("%d <=> ", cur->data);
    61. //打印完当前结点数据域数据后cur移向下个结点:
    62. cur = cur->next;
    63. }
    64. //打印完一个链表后换行:
    65. printf("\n");
    66. }
    67. //向链表尾部插入一个结点(尾插):
    68. //接收结点头指针(phead)、要尾插进链表的值(x)
    69. void LTPushBack(LTNode* phead, LTDataType x)
    70. {
    71. //assert断言头指针(哨兵位地址)不为空:
    72. assert(phead != NULL);
    73. //通过哨兵位找到尾结点:
    74. //因为是循环链表:
    75. //所以哨兵位(带头链表)的前一个结点就是尾结点
    76. LTNode* tail = phead->prev;
    77. //调用前驱指针获得尾结点
    78. //调用BuyLTNode函数为尾插创建尾插结点newnode:
    79. LTNode* newnode = BuyLTNode(x);
    80. //尾插结点前驱指针prev指向原本的尾结点:
    81. newnode->prev = tail;
    82. //原本尾结点后继指针next指向尾插结点:
    83. tail->next = newnode;
    84. //尾插结点后继指针next指向头结点:
    85. newnode->next = phead;
    86. //头结点前驱指针指向尾插结点:
    87. phead->prev = newnode;
    88. //带头+双向+循环 链表:
    89. //对比单链表,因为有哨兵位不用考虑链表为空的情况,
    90. //且不需要二级指针,通过操纵哨兵位这个结构体,
    91. //替换用二级指针操作头指针的操作
    92. /*
    93. //第二种方法:复用LTInsert函数
    94. //在哨兵位前插入一个值x就是尾插了:
    95. LTInsert(phead, x);
    96. */
    97. }
    98. //双向链表尾删函数--删除链表尾部结点(尾删)
    99. //接收链表头指针(phead)
    100. void LTPopBack(LTNode* phead)
    101. {
    102. //assert断言头指针(哨兵位地址)不为空:
    103. assert(phead != NULL);
    104. //assert断言双向链表不是空链表:
    105. assert(phead->next != phead);
    106. //如果哨兵位的下一个结点还是哨兵位说明是空链表
    107. //通过哨兵位的前驱指针prev获得尾结点tail:
    108. LTNode* tail = phead->prev;
    109. //再通过尾结点tail获得倒数第二个结点tailPrev:
    110. LTNode* tailPrev = tail->prev;
    111. //释放(删除)尾结点tail:
    112. free(tail);
    113. //这时就让倒数第二个结点tailPrev成为新的尾结点
    114. //为保持循环,把tailPrev和哨兵位连接起来:
    115. tailPrev->next = phead; //tailPrev后继指针指向哨兵位
    116. phead->prev = tailPrev; //哨兵位前驱指针指向tailPrev
    117. //带头+双向+循环 链表:
    118. //对比单链表,这里双向链表在尾删时因为有哨兵位的存在
    119. //即使链表只剩一个结点,也不用进行判断单独处理进行置空
    120. //这一个结点删掉后,还有哨兵位存在
    121. /*
    122. //第二种方法:复用LTErase函数
    123. //传尾结点地址给LTErase函数即可:
    124. LTErase(phead->prev);
    125. */
    126. }
    127. //双向链表头插函数--向链表头部插入一个结点(头插):
    128. //接收链表头指针(phead)、要头插进链表的值(x)
    129. void LTPushFront(LTNode* phead, LTDataType x)
    130. {
    131. //第二种方法:多定义一个指针
    132. //assert断言头指针(哨兵位地址)不为空:
    133. assert(phead != NULL);
    134. //调用BuyLTNode函数为头插创建头插结点newnode:
    135. LTNode* newnode = BuyLTNode(x);
    136. //创建一个first指针保存原本第一个结点地址:
    137. LTNode* first = phead->next;
    138. //哨兵位后继指针next指向头插结点newnode:
    139. phead->next = newnode;
    140. //头插结点newnode前驱指针prev指向哨兵位:
    141. newnode->prev = phead;
    142. //头插结点newnode后继指针next指向原本头结点first:
    143. newnode->next = first;
    144. //原本头结点first前驱指针prev指向头插结点newnode:
    145. first->prev = newnode;
    146. /*
    147. //assert断言头指针(哨兵位地址)不为空:
    148. assert(phead != NULL);
    149. //第一种方法:需要注意连接的顺序
    150. //调用BuyLTNode函数为头插创建头插结点newnode:
    151. LTNode* newnode = BuyLTNode(x);
    152. //先将头插结点的后继节点next连接上原本头结点:
    153. newnode->next = phead->next;
    154. //哨兵位的后继指针指向的就是头结点
    155. //再将原本头结点的前驱指针prev指向头插结点newnode:
    156. phead->next->prev = newnode;
    157. //哨兵位连接上头插节点newnode:
    158. phead->next = newnode;
    159. //头插结点newnode的前驱指针指向哨兵位:
    160. newnode->prev = phead;
    161. */
    162. /*
    163. //第三种方法:复用LTInsert函数
    164. //在哨兵位后一个结点前插入一个值x就是头插了:
    165. LTInsert(phead->next, x);
    166. */
    167. }
    168. //双向链表头删函数--删除链表头部结点(头删)
    169. //接收链表头指针(phead)
    170. void LTPopFront(LTNode* phead)
    171. {
    172. //assert断言头指针(哨兵位地址)不为空:
    173. assert(phead != NULL);
    174. //assert断言双向链表不是空链表:
    175. assert(phead->next != phead);
    176. //如果哨兵位的下一个结点还是哨兵位说明是空链表
    177. //通过哨兵位后继结点next获得头结点地址:
    178. LTNode* first = phead->next;
    179. //再通过first结点获得第二个结点:
    180. LTNode* second = first->next;
    181. //释放头结点first:
    182. free(first);
    183. //让哨兵位后继结点next指向第二个结点second:
    184. phead->next = second;
    185. //第二个结点的前驱指针prev指向哨兵位:
    186. second->prev = phead;
    187. //带头+双向+循环 链表:
    188. //对比单链表,这里双向链表在头删时因为有哨兵位的存在
    189. //即使链表只剩一个结点,也不用进行判断单独处理进行置空
    190. //这一个结点删掉后,还有哨兵位存在
    191. /*
    192. //第二种方法:复用LTErase函数
    193. //传第一个结点地址给LTErase函数即可:
    194. LTErase(phead->next);
    195. */
    196. }
    197. //求链表结点个数函数
    198. //接收链表头指针(phead)
    199. int LTSize(LTNode* phead)
    200. {
    201. //assert断言头指针(哨兵位地址)不为空:
    202. assert(phead != NULL);
    203. int size = 0; //存放链表长度
    204. //创建结点指针cur进行遍历:
    205. //指针cur应该从哨兵位(头指针)下一个结点开始
    206. LTNode* cur = phead->next;
    207. //因为是循环链表,不是以NULL空指针结尾
    208. //所以应该是当指针cur遍历回到哨兵位就终止遍历:
    209. while (cur != phead)
    210. //如果只有哨兵位,链表为空,
    211. //phead->next还是phead,不会进行打印
    212. {
    213. ++size; //遍历一遍长度+1
    214. //cur移向下个结点:
    215. cur = cur->next;
    216. }
    217. //返回链表长度:
    218. return size;
    219. }
    220. //双向链表查找函数--在双向链表中查找数据域数据为x的结点地址
    221. //接收链表头指针(phead)、要在链表中查找的值(x)
    222. LTNode* LTFind(LTNode* phead, LTDataType x)
    223. {
    224. //assert断言头指针(哨兵位地址)不为空:
    225. assert(phead != NULL);
    226. //创建遍历指针cur,从第一个结点开始:
    227. LTNode* cur = phead->next;
    228. //使用while循环进行遍历:
    229. while (cur != phead)
    230. //如果只有哨兵位,链表为空,
    231. //phead->next还是phead,不会进行打印
    232. {
    233. if (cur->data == x)
    234. //找到要找的值了:
    235. {
    236. return cur; //返回该值结点
    237. }
    238. //调整指针:
    239. cur = cur->next;
    240. }
    241. //未找到则返回空指针:
    242. return NULL;
    243. }
    244. //双向链表插入函数--在pos结点之前插入数据域数据为x的结点
    245. //接收插入位置(pos)、要插入链表的值(x)
    246. void LTInsert(LTNode* pos, LTDataType x)
    247. {
    248. //assert断言插入位置pos不为空:
    249. assert(pos != NULL);
    250. //通过pos结点获得前一个结点posPrev地址:
    251. LTNode* posPrev = pos->prev;
    252. //使用BuyLTNode函数为插入结点开辟空间:
    253. LTNode* newnode = BuyLTNode(x);
    254. //posPrev结点的后继指针next指向newnode:
    255. posPrev->next = newnode;
    256. //newnode前驱指针prev指向posPrev:
    257. newnode->prev = posPrev;
    258. //newnode后继指针next指向pos:
    259. newnode->next = pos;
    260. //pos结点前驱指针prev指向newnode:
    261. pos->prev = newnode;
    262. }
    263. //双向链表删除函数--删除pos结点
    264. //接收要删除结点地址(pos)
    265. void LTErase(LTNode* pos)
    266. {
    267. //assert断言删除位置结点pos不为空:
    268. assert(pos != NULL);
    269. //保存要删除结点pos的前一个结点posPrev地址:
    270. LTNode* posPrev = pos->prev;
    271. //保存要删除结点pos的后一个结点posNext地址:
    272. LTNode* posNext = pos->next;
    273. //释放掉pos结点:
    274. free(pos);
    275. //将pos前结点posPrev的后继指针指向posNext:
    276. posPrev->next = posNext;
    277. //将pos后结点posNext的前驱指针指向posPrev:
    278. posNext->prev = posPrev;
    279. }
    280. //双向链表销毁函数--销毁链表
    281. //接收要销毁链表头系欸但(phead)
    282. void LTDestroy(LTNode* phead)
    283. {
    284. //assert断言头指针(哨兵位地址)不为空:
    285. assert(phead != NULL);
    286. //创建遍历指针cur,从第一个结点开始:
    287. LTNode* cur = phead->next;
    288. //使用while循环进行遍历释放:
    289. while (cur != phead)
    290. {
    291. //释放前先存储下一个结点地址:
    292. LTNode* next = cur->next;
    293. //释放当前结点:
    294. free(cur);
    295. //调整指针:
    296. cur = next;
    297. }
    298. //删除完有效结点后,最后再释放哨兵位:
    299. free(phead);
    300. }

                

                

    ---------------------------------------------------------------------------------------------

                    

    Test.c -- 双向链表测试文件

    1. #define _CRT_SECURE_NO_WARNINGS 1
    2. //双向链表函数测试文件:
    3. //包含双向链表头文件:
    4. #include "List.h"
    5. //测试函数--
    6. //LTInit、LTPushBack、LTPrintf函数
    7. void TestList1()
    8. {
    9. //初始化一个双向链表:
    10. LTNode* plist = LTInit();
    11. //初始化后使用尾插函数插入数据:
    12. LTPushBack(plist, 1);
    13. LTPushBack(plist, 2);
    14. LTPushBack(plist, 3);
    15. LTPushBack(plist, 4);
    16. LTPushBack(plist, 5);
    17. //打印当前双向链表:
    18. LTPrint(plist);
    19. }
    20. //测试函数--LTPopBack函数
    21. void TestList2()
    22. {
    23. //初始化一个双向链表:
    24. LTNode* plist = LTInit();
    25. //初始化后使用尾插函数插入数据:
    26. LTPushBack(plist, 1);
    27. LTPushBack(plist, 2);
    28. LTPushBack(plist, 3);
    29. //打印当前双向链表:
    30. LTPrint(plist);
    31. //进行尾删:
    32. LTPopBack(plist);
    33. //打印当前双向链表:
    34. LTPrint(plist);
    35. //进行尾删:
    36. LTPopBack(plist);
    37. //打印当前双向链表:
    38. LTPrint(plist);
    39. //进行尾删:
    40. LTPopBack(plist);
    41. //打印当前双向链表:
    42. LTPrint(plist);
    43. }
    44. //测试函数--LTPushFront函数
    45. void TestList3()
    46. {
    47. //初始化一个双向链表:
    48. LTNode* plist = LTInit();
    49. //初始化后使用尾插函数插入数据:
    50. LTPushBack(plist, 1);
    51. LTPushBack(plist, 2);
    52. LTPushBack(plist, 3);
    53. //打印当前双向链表:
    54. LTPrint(plist);
    55. //进行头插:
    56. LTPushFront(plist, 1000);
    57. //打印当前双向链表:
    58. LTPrint(plist);
    59. }
    60. //测试函数--LTPopFront函数
    61. void TestList4()
    62. {
    63. //初始化一个双向链表:
    64. LTNode* plist = LTInit();
    65. //初始化后使用尾插函数插入数据:
    66. LTPushBack(plist, 1);
    67. LTPushBack(plist, 2);
    68. LTPushBack(plist, 3);
    69. //打印当前双向链表:
    70. LTPrint(plist);
    71. //进行头删:
    72. LTPopFront(plist);
    73. //打印当前双向链表:
    74. LTPrint(plist);
    75. }
    76. //测试函数--LTSize函数
    77. void TestList5()
    78. {
    79. //初始化一个双向链表:
    80. LTNode* plist = LTInit();
    81. //初始化后使用尾插函数插入数据:
    82. LTPushBack(plist, 1);
    83. LTPushBack(plist, 2);
    84. LTPushBack(plist, 3);
    85. //打印当前双向链表:
    86. LTPrint(plist);
    87. //计算链表长度:
    88. int size = LTSize(plist);
    89. //打印当前双向链表:
    90. printf("链表长度为:%d", size);
    91. }
    92. //测试函数--LTFind函数
    93. void TestList6()
    94. {
    95. //初始化一个双向链表:
    96. LTNode* plist = LTInit();
    97. //初始化后使用尾插函数插入数据:
    98. LTPushBack(plist, 1);
    99. LTPushBack(plist, 2);
    100. LTPushBack(plist, 3);
    101. //打印当前双向链表:
    102. LTPrint(plist);
    103. //使用查找函数:
    104. LTNode* find = LTFind(plist, 2);
    105. //打印找到的地址
    106. printf("0x%xn", find);
    107. }
    108. //测试函数--LTInsert函数
    109. void TestList7()
    110. {
    111. //初始化一个双向链表:
    112. LTNode* plist = LTInit();
    113. //初始化后使用尾插函数插入数据:
    114. LTPushBack(plist, 1);
    115. LTPushBack(plist, 2);
    116. LTPushBack(plist, 3);
    117. //打印当前双向链表:
    118. LTPrint(plist);
    119. //使用插入函数:
    120. LTInsert(plist->next->next, 100);
    121. //打印当前双向链表:
    122. LTPrint(plist);
    123. }
    124. //测试函数--LTErase函数
    125. void TestList8()
    126. {
    127. //初始化一个双向链表:
    128. LTNode* plist = LTInit();
    129. //初始化后使用尾插函数插入数据:
    130. LTPushBack(plist, 1);
    131. LTPushBack(plist, 2);
    132. LTPushBack(plist, 3);
    133. //打印当前双向链表:
    134. LTPrint(plist);
    135. //使用删除函数:
    136. LTErase(plist->next->next);
    137. //打印当前双向链表:
    138. LTPrint(plist);
    139. }
    140. //主函数:
    141. int main()
    142. {
    143. //调用测试函数:
    144. //TestList1();
    145. //TestList2();
    146. //TestList3();
    147. //TestList4();
    148. //TestList5();
    149. //TestList6();
    150. //TestList7();
    151. TestList8();
    152. return 0;
    153. }
  • 相关阅读:
    北斗导航 | RAIM算法之最小二乘残差法(原理讲解,附代码链接:可用性判定)
    YOLO算法(You Only Look Once)系列讲解与实现(待完善)
    Spring-AOP入门
    手撕IP核系列——Xilinx FIFO IP核-同步FIFO
    Vue 实现拖拽模块(二)自定义拖拽组件位置
    Hexagon_V65_Programmers_Reference_Manual (50)
    书店系统的设计与实现
    win下搭建php环境的方法
    c语言进阶部分详解(指针进阶1)
    【蓝桥杯真题练习】STEMA科技素养练习题库 答案版012 持续更新中~
  • 原文地址:https://blog.csdn.net/weixin_63176266/article/details/133018982