• 数据结构——双向循环链表


    目录

    结构体的建立

    初始化

    创建节点 

     头插节点 

    尾插节点 

    头删节点 

    尾删节点 

     节点查找

    节点前插入

     节点删除

     销毁链表

     Dlist.h

     Dlist.c

    顺序表和链表的区别 


    结构体的建立

    1. typedef int LTDatatype;
    2. typedef struct ListNode
    3. {
    4. struct ListNode* next;
    5. struct ListNode* prev;
    6. LTDatatype data;
    7. }LTNode;

    初始化

    1. /*void ListInit(LTNode**pphead);*///初始化,初始化要传指针,要建立一个哨兵节点,要实质上把plist改变
    2. LTNode* ListInit();//初始化,也可以不用二级,用一个返回值就行
    3. LTNode* ListInit()
    4. {
    5. LTNode* guard= (LTNode*)malloc(sizeof(LTNode));
    6. if (guard == NULL)
    7. {
    8. return NULL;
    9. }
    10. guard->next = guard;
    11. guard->prev = guard;
    12. return guard;
    13. }

    创建节点 

    1. LTNode* BuyNode(LTDatatype x)
    2. {
    3. LTNode* guard = (LTNode*)malloc(sizeof(LTNode));
    4. if (guard == NULL)
    5. {
    6. return NULL;
    7. }
    8. guard->next = NULL;
    9. guard->data = x;
    10. guard->prev = NULL;
    11. return guard;
    12. }

     头插节点 

    1. void ListPushFront(LTNode* pphead, LTDatatype x)
    2. {
    3. LTNode* newnode = BuyNode(x);
    4. LTNode* next = pphead->next;
    5. newnode->next = next;
    6. next->prev = newnode;
    7. newnode->prev = pphead;
    8. pphead->next = newnode;
    9. }

    尾插节点 

    1. void ListPushBack(LTNode* pphead, LTDatatype x)
    2. {
    3. assert(pphead);//pphead不可能为空
    4. LTNode* newnode = BuyNode(x);
    5. LTNode* tail = pphead->prev;//先找到尾
    6. tail->next = newnode;//开始尾插
    7. newnode->prev = tail;
    8. newnode->next = pphead;
    9. pphead->prev =newnode;
    10. }//这种写法即使pphead是NULL,依然能插入,因为通过tail找到了pphead

    头删节点 

    1. void ListPopFront(LTNode* pphead)
    2. {
    3. LTNode* next = pphead->next;
    4. pphead->next = next->next;
    5. next->next->prev = pphead;
    6. free(next);
    7. }

    尾删节点 

    1. void ListPopBack(LTNode* pphead)
    2. {
    3. assert(pphead);
    4. assert(!ListEmpty(pphead));//判断是否为空
    5. LTNode* tail1 = pphead->prev;
    6. LTNode* tail2 = tail1->prev;
    7. free(tail1);
    8. tail1 = NULL;
    9. tail2->next = pphead;
    10. pphead->prev = tail2;
    11. }
    12. bool ListEmpty(LTNode* pphead)
    13. {
    14. assert(pphead);
    15. return pphead->next == pphead;//检测是否只剩头节点
    16. }

     节点查找

    1. LTNode* ListFind(LTNode* pphead,LTDatatype x)
    2. {
    3. assert(pphead);
    4. size_t n = 0;
    5. LTNode* cur = pphead->next;
    6. while (cur != pphead)
    7. {
    8. if (cur->data == x)
    9. {
    10. return cur;
    11. }
    12. }
    13. }

    节点前插入

    1. void ListInsert(LTNode* pos, LTDatatype x)//在pos之前插入,若在pphead前插入,实际会变成尾插
    2. {
    3. assert(pos);
    4. LTNode* prev = pos->prev;
    5. LTNode* newnode = BuyNode(x);
    6. prev->next = newnode;
    7. newnode->prev = prev;
    8. newnode->next = pos;
    9. pos->prev = newnode;
    10. }

     节点删除

    1. void ListErase(LTNode* pos)
    2. {
    3. assert(pos);
    4. LTNode* prev = pos->prev;
    5. LTNode* next = pos->next;
    6. prev->next = next;
    7. next->prev = prev;
    8. free(pos);
    9. pos = NULL;
    10. }

     销毁链表

    1. void ListDestory(LTNode* pphead)//传一级,二级都可以
    2. {
    3. assert(pphead);
    4. //1.先释放带数字的节点,最后释放头哨兵
    5. LTNode* cur = pphead->next;
    6. while (cur != pphead)
    7. {
    8. LTNode* next = cur->next;
    9. free(cur);
    10. cur = next;
    11. }
    12. free(pphead);
    13. pphead = NULL;//如果传一级指针,这里的置空不起作用,若想起作用,要传二级指针
    14. }
    15. //建议:使用一级指针,然后让调用ListDestory的人置空,保持接口的一致性,这里最后回到主函数把plist=NULL;即可

     Dlist.h

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. typedef int LTDatatype;
    8. typedef struct ListNode
    9. {
    10. struct ListNode* next;
    11. struct ListNode* prev;
    12. LTDatatype data;
    13. }LTNode;
    14. /*void ListInit(LTNode**pphead);*///初始化,初始化要传指针,要建立一个哨兵节点,要实质上把plist改变
    15. LTNode* ListInit();//初始化,也可以不用二级,用一个返回值就行
    16. void ListPushBack(LTNode* pphead,LTDatatype x);//不需要传地址,因为再怎么改变都不会改变哨兵位的头,改的是哨兵位的指向
    17. void ListPushFront(LTNode* pphead, LTDatatype x);
    18. void ListPopBack(LTNode* pphead);
    19. void ListPrint(LTNode* pphead);
    20. void ListPopFront(LTNode* pphead);
    21. bool ListEmpty(LTNode* pphead);
    22. size_t ListSize(LTNode* pphead);
    23. LTNode* ListFind(LTNode* pphead, LTDatatype x);
    24. void ListInsert(LTNode* pos, LTDatatype x);
    25. void ListErase(LTNode* pos);
    26. void ListDestory(LTNode* pphead);

     Dlist.c

    1. #define _CRT_SECURE_NO_WARNINGS
    2. #include"Dlist.h"
    3. LTNode* ListInit()
    4. {
    5. LTNode* guard= (LTNode*)malloc(sizeof(LTNode));
    6. if (guard == NULL)
    7. {
    8. return NULL;
    9. }
    10. guard->next = guard;
    11. guard->prev = guard;
    12. return guard;
    13. }
    14. LTNode* BuyNode(LTDatatype x)
    15. {
    16. LTNode* guard = (LTNode*)malloc(sizeof(LTNode));
    17. if (guard == NULL)
    18. {
    19. return NULL;
    20. }
    21. guard->next = NULL;
    22. guard->data = x;
    23. guard->prev = NULL;
    24. return guard;
    25. }
    26. void ListPrint(LTNode* pphead)
    27. {
    28. assert(pphead);
    29. printf("guard<=>");
    30. LTNode* cur = pphead->next;
    31. while (cur!= pphead)
    32. {
    33. printf("%d<=>", cur->data);
    34. cur= cur->next;
    35. }
    36. printf("\n");
    37. }
    38. void ListPushBack(LTNode* pphead, LTDatatype x)
    39. {
    40. assert(pphead);//pphead不可能为空
    41. LTNode* newnode = BuyNode(x);
    42. LTNode* tail = pphead->prev;//先找到尾
    43. tail->next = newnode;//开始尾插
    44. newnode->prev = tail;
    45. newnode->next = pphead;
    46. pphead->prev =newnode;
    47. }//这种写法即使pphead是NULL,依然能插入,因为通过tail找到了pphead
    48. void ListPushFront(LTNode* pphead, LTDatatype x)
    49. {
    50. LTNode* newnode = BuyNode(x);
    51. LTNode* next = pphead->next;
    52. newnode->next = next;
    53. next->prev = newnode;
    54. newnode->prev = pphead;
    55. pphead->next = newnode;
    56. }
    57. void ListPopBack(LTNode* pphead)
    58. {
    59. assert(pphead);
    60. assert(!ListEmpty(pphead));//判断是否为空
    61. LTNode* tail1 = pphead->prev;
    62. LTNode* tail2 = tail1->prev;
    63. free(tail1);
    64. tail1 = NULL;
    65. tail2->next = pphead;
    66. pphead->prev = tail2;
    67. }
    68. bool ListEmpty(LTNode* pphead)
    69. {
    70. assert(pphead);
    71. return pphead->next == pphead;//检测是否只剩头节点
    72. }
    73. void ListPopFront(LTNode* pphead)
    74. {
    75. LTNode* next = pphead->next;
    76. pphead->next = next->next;
    77. next->next->prev = pphead;
    78. free(next);
    79. }
    80. size_t ListSize(LTNode* pphead)
    81. {
    82. size_t n = 0;
    83. LTNode* cur = pphead->next;
    84. while (cur != pphead)
    85. {
    86. ++n;
    87. cur = cur->next;
    88. }
    89. return n;
    90. }
    91. LTNode* ListFind(LTNode* pphead,LTDatatype x)
    92. {
    93. assert(pphead);
    94. size_t n = 0;
    95. LTNode* cur = pphead->next;
    96. while (cur != pphead)
    97. {
    98. if (cur->data == x)
    99. {
    100. return cur;
    101. }
    102. }
    103. }
    104. void ListInsert(LTNode* pos, LTDatatype x)//在pos之前插入,若在pphead前插入,实际会变成尾插
    105. {
    106. assert(pos);
    107. LTNode* prev = pos->prev;
    108. LTNode* newnode = BuyNode(x);
    109. prev->next = newnode;
    110. newnode->prev = prev;
    111. newnode->next = pos;
    112. pos->prev = newnode;
    113. }
    114. void ListErase(LTNode* pos)
    115. {
    116. assert(pos);
    117. LTNode* prev = pos->prev;
    118. LTNode* next = pos->next;
    119. prev->next = next;
    120. next->prev = prev;
    121. free(pos);
    122. pos = NULL;
    123. }
    124. void ListDestory(LTNode* pphead)//传一级,二级都可以
    125. {
    126. assert(pphead);
    127. //1.先释放带数字的节点,最后释放头哨兵
    128. LTNode* cur = pphead->next;
    129. while (cur != pphead)
    130. {
    131. LTNode* next = cur->next;
    132. free(cur);
    133. cur = next;
    134. }
    135. free(pphead);
    136. pphead = NULL;//如果传一级指针,这里的置空不起作用,若想起作用,要传二级指针
    137. }
    138. //建议:使用一级指针,然后让调用ListDestory的人置空,保持接口的一致性,这

    顺序表和链表的区别 

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

    顺序表优点:1.尾插尾删的效率很高

                          2.可以随机访问(通过下标),链表需要遍历

                          3.和链表相比cpu高速缓存面中率很高

    寄存器相当于锅 ,如有数组{1,2,3,4},这四个元素是存在主存里面的,数据结构是在主存里面管理我们的数据,链表也一样(堆是一个虚拟的内存的划分)

    越往上速度越快,成本更高 ,L1,L2,L3被称为CPU的高速缓存

    代码编译好了就是指令,通过CPU去运行指令,CPU访问通过访问内存数据运行指令,

    CPU执行指令,不会直接访问内存

    1.先看数据在不在三级缓存,在(命中),就直接访问

    2.不在(不命中),先加载到缓存,再访问

    顺序表的缺点:1.头部和中部的插入删除效率低——O(N)

                             2.扩容(动态开辟空间)——性能消耗,有一定空间浪费

    链表的优点:1.任意位置插入删除效率很高O(1)

                          2.按需申请释放(不存在空间浪费)

    链表的缺点:1.不支持随机访问(现实中随机访问很重要)

                 

  • 相关阅读:
    反射技巧让你的性能提升N倍
    深度学习_6_实战_点集最优直线解_代码解析
    数据结构(一)线性表(模板实现)
    【洛谷】P5149 会议座位
    导数差分近似公式总结
    高可用架构,去中心化有多重要?
    MySQL查询数据库响应时长的方法
    VLAN笔记
    系统架构设计师(第二版)学习笔记----层次式架构设计理论与实践
    Typora使用教程
  • 原文地址:https://blog.csdn.net/weixin_49449676/article/details/126146839