• Day 62 单向循环链表 双链表


    1.单向循环链表

    概念:

    如果把单 链表的 最后一个节点的指针指向链表头部,而不是指向 NULL ,那么就构成了一个单向循环链表,通俗讲就是把尾节点的下一跳指向头结点使其形成一个闭环。
    使用原因:
    单向链表 中,头指针是相当重要的,因为单向链表的操作都需要头指针,所以如果头指针丢失或者破坏,那么整个链表都会遗失,并且浪费链表内存空间,因此我们引入了单向循环链表这种数据结构。
    使用时要注意的问题:
    1. 在链表中我们使用的是虚拟头结点,但是在循环链表中我们在遍历的时候就会遇到麻烦,因此在单向循环链表中我们使用的是真实头结点。
    2. 循环单链表没有明显的结束条件,当我们不注意时,很容易陷入死循环。此时,我们可以设置一个标记点作为循环的标记,当我们知道表长,我们也可以设置一个计数器来结束循环。
    单向循环链表:中间插法

    设计一个单向循环链表的过程:

    1. 定义链表结点:数据域 + 指针域
    2. 定义链表结构体:头结点指针
    3. 初始化链表
    4. 指定位置插入新数据 ( 头插法,中间插法 )
    5. 删除指定位置数据
    6. 获取链表长度
    7. 依据数据查找所在链表位置
    8. 返回第一个结点
    9. 打印链表结点数据
    10. 释放链表
    下面是对一个单向循环链表的增删改查:
    1. #include
    2. #include
    3. #include
    4. struct node{
    5. int data;
    6. struct node *next;
    7. };
    8. typedef struct node link_t;
    9. link_t *link_init(void){
    10. link_t *p=(link_t *)malloc(sizeof(link_t));
    11. if(p==NULL){
    12. perror("malloc error");
    13. return NULL;
    14. }
    15. p->next=p;
    16. return p;
    17. }
    18. static void insert_behind(link_t *p,link_t *node){
    19. node->next=p->next;
    20. p->next=node;
    21. }
    22. void link_add_tail(link_t *p,int d){
    23. link_t *head=p;
    24. link_t *node=(link_t *)malloc(sizeof(link_t));
    25. if(node==NULL){
    26. return;
    27. }
    28. node->data=d;
    29. while(p->next!=head){
    30. p=p->next;
    31. }
    32. insert_behind(p,node);
    33. }
    34. void link_del(link_t *p,int d){
    35. link_t *head=p;
    36. link_t *delnode=NULL;
    37. while(p->next!=head){
    38. if(p->next->data==d){
    39. delnode=p->next;
    40. p->next=delnode->next;
    41. free(delnode);
    42. delnode=NULL;
    43. continue;
    44. }
    45. p=p->next;
    46. }
    47. }
    48. void link_update(link_t *p,int old,int new){
    49. link_t *head=p;
    50. link_t *node=NULL;
    51. link_t *temp=NULL;
    52. while(p->next!=head){
    53. if(p->next->data==old){
    54. p->next->data=new;
    55. }
    56. p=p->next;
    57. }
    58. }
    59. int link_find(link_t *p,int d){
    60. link_t *head=p;
    61. int count=0;
    62. while(p->next!=head){
    63. if(p->next->data==d){
    64. return count+1;
    65. }
    66. count++;
    67. p=p->next;
    68. }
    69. return -1;
    70. }
    71. void display(link_t *p){
    72. link_t *head=p;
    73. printf("遍历结果为:");
    74. while(p->next!=head){
    75. p=p->next;
    76. printf("%d->",p->data);
    77. }
    78. printf("\n");
    79. }
    80. int main(){
    81. int i;
    82. link_t *phead=link_init();
    83. for(i=0;i<6;i++){
    84. link_add_tail(phead,i);
    85. }
    86. display(phead);
    87. link_del(phead,3);
    88. display(phead);
    89. int ret=link_find(phead,4);
    90. printf("找到节点的位置是:%d\n",ret);
    91. display(phead);
    92. return 0;
    93. }

    2.双链表:

    概念:就是在单链表的的每个结点中,再设置一个指针域,也就是每个结点都有两个指针域,一个指向它的前驱结点,一个指向它的后继结点。

    使用原因:

    虽然使用单链表能 100% 解决逻辑关系为 " 一对一 " 数据的存储问题,但在解决某些特殊问题时,单链表并不是效率最优的存储结构。比如说,如果算法中需要大量地找某指定结点的前趋结点,使用单链表无疑是灾难性的,因为单链表更适合 " 从前往后 " 找,而 " 从后往前 " 找并不是它的强项。所以为了实现“从后往前找”的功能,使用双链表。
    结构:
    1. 指针域:用于指向当前节点的直接前驱节点;
    2. 数据域:用于存储数据元素。
    3. 指针域:用于指向当前节点的直接后继节点;
    以下是对一个双链表的增删改查:
    1. #include
    2. #include
    3. #include
    4. /* 双向链表的结构 */
    5. struct List
    6. {
    7. int data; //数据域
    8. struct List *next; //向后的指针
    9. struct List *front; //向前的指针
    10. };
    11. typedef struct List NODE;
    12. /*
    13. * 双向链表的创建
    14. *
    15. */
    16. NODE *CreatList()
    17. {
    18. NODE *head = (NODE *)malloc(sizeof(NODE));
    19. if(head == NULL)
    20. {
    21. printf("malloc error\n");
    22. return NULL;
    23. }
    24. head->next = head->front = NULL; //初始化链表,指针置空
    25. return head;
    26. }
    27. /*
    28. * 双向链表插入节点,头插法
    29. */
    30. void InsertNode(NODE *head , int data)
    31. {
    32. if( head == NULL )
    33. {
    34. printf("The list is empty.\n"); // 头结点为空,则无法插入
    35. return;
    36. }
    37. NODE *tmpHead = head; // 创建一个临时的头结点指针
    38. if( tmpHead->next == NULL )
    39. {
    40. /* 当双向链表只有一个头结点时 */
    41. NODE *addition = (NODE *)malloc(sizeof(NODE));
    42. if( addition == NULL )
    43. {
    44. printf("malloc error\n");
    45. return;
    46. }
    47. addition->data = data; //数据域赋值
    48. addition->next = tmpHead->next; //后向指针连接
    49. tmpHead->next = addition;
    50. addition->front = tmpHead; //将插入结点的front 指向头结点
    51. }
    52. else
    53. {
    54. /* 当双向链表不只一个头结点时 */
    55. NODE *addition = (NODE *)malloc( sizeof(NODE));
    56. if( addition == NULL )
    57. {
    58. printf("malloc error\n");
    59. return;
    60. }
    61. addition->data = data; // 数据域赋值
    62. tmpHead->next->front = addition; // 头结点的下一个结点的front 指针
    63. addition->front = tmpHead; // 插入的结点的front 指针指向头结点
    64. addition->next = tmpHead->next; // 插入结点的next 指针指向原本头指针的下
    65. 一结点
    66. tmpHead->next = addition; // 将头结点的next 指针指向插入结点
    67. }
    68. }
    69. /*
    70. * 删除一个节点
    71. */
    72. void DeleteNode(NODE *head , int data)
    73. {
    74. if(head == NULL)
    75. {
    76. printf("The list is empty.\n");
    77. return;
    78. }
    79. NODE *tmpHead = head;
    80. while( tmpHead->next != NULL )
    81. {
    82. tmpHead = tmpHead->next;
    83. if( tmpHead->data == data )
    84. {
    85. tmpHead->front->next = tmpHead->next; // 将被删结点的上一个结
    86. 点的next 指针指向 被删结点的下一个结点
    87. tmpHead->next->front = tmpHead->front; // 将被删结点的下一个结
    88. 点的 front 指针指向 被删结点的上一个结点
    89. break;
    90. }
    91. }
    92. free(tmpHead); //释放内存
    93. tmpHead = NULL;
    94. }
    95. /*
    96. * 修改一个节点的值
    97. */
    98. void UpdateNode(NODE *head , int oldval,int newval)
    99. {
    100. if(head == NULL)
    101. {
    102. printf("The list is empty.\n");
    103. return;
    104. }
    105. NODE *tmpHead = head;
    106. while( tmpHead->next != NULL )
    107. {
    108. tmpHead = tmpHead->next;
    109. if(tmpHead->data == oldval)
    110. {
    111. tmpHead->data == newval;
    112. continue;
    113. }
    114. }
    115. }
    116. /*
    117. * 查找值为val的节点位置
    118. */
    119. int FindNode(NODE *head , int val)
    120. {
    121. int count = 0;
    122. if(head == NULL)
    123. {
    124. printf("The list is empty.\n");
    125. return -1;
    126. }
    127. NODE *tmpHead = head;
    128. while( tmpHead->next != NULL )
    129. {
    130. tmpHead = tmpHead->next;
    131. if(tmpHead->data == val)
    132. {
    133. return count;
    134. }
    135. count++;
    136. }
    137. return -1;
    138. }
    139. /*
    140. * 双向链表的遍历
    141. */
    142. void displayList(NODE *head)
    143. {
    144. printf("-------------------------------\n");
    145. if( head == NULL )
    146. {
    147. printf("The list is empty.\n");
    148. return;
    149. }
    150. NODE *tmpHead = head; //头节点没有数据
    151. while(tmpHead->next != NULL )
    152. {
    153. tmpHead = tmpHead->next; //头结点数据域为空,因此直接从头结点的下一结点开
    154. 始遍历
    155. printf("%d->",tmpHead->data);
    156. }
    157. // 此时tmpHead 的地址在链表的最后一个结点处
    158. while( tmpHead->front->front != NULL )
    159. {
    160. printf("%d<-",tmpHead->data); // 最后一个结点要输出
    161. tmpHead = tmpHead->front;
    162. }
    163. printf("%d\n",tmpHead->data);
    164. return;
    165. }
    166. /*
    167. * 销毁一个链表,从头结点开始
    168. */
    169. void DestroyList(NODE *head)
    170. {
    171. NODE *tmp;
    172. while(head->next != NULL)
    173. {
    174. tmp = head; // 指针不断后移
    175. head = head->next;
    176. free(tmp);
    177. tmp = NULL;
    178. }
    179. free(head);
    180. head = NULL;
    181. }
    182. int main()
    183. {
    184. //创建一个双向链表
    185. NODE *phead = CreatList();
    186. if(phead == NULL)
    187. {
    188. printf("create list error\n");
    189. }
    190. //双向链表的头部插入数据
    191. int i;
    192. for(i=0;i<6;i++)
    193. {
    194. InsertNode(phead , i);
    195. }
    196. //显示链表的数据
    197. displayList(phead);
    198. //删除链表中的一个节点
    199. DeleteNode(phead , 4);
    200. displayList(phead);
    201. //修改链表中的一个节点的值
    202. UpdateNode(phead , 2,222);
    203. displayList(phead);
    204. //查找链表中的一个值
    205. int ret = FindNode(phead , 3);
    206. printf("要找的节点是:%d\n",ret);
    207. return 0;
    208. }

  • 相关阅读:
    k8s开放接口
    leetcode竞赛:85 场双周赛
    【LeetCode】78. 子集
    python学习路线图(初级阶段,中级阶段,高级阶段)
    基于java+springboot+mybatis+vue+elementui的毕业生信息招聘平台
    面试经典150题 -- 二叉树 (总结)
    Linux CentOS7 wc命令
    (十七)STM32——定时器
    超纯水制备
    Mybaits一级缓存和二级缓存分别是什么,区别是什么?
  • 原文地址:https://blog.csdn.net/m0_60247706/article/details/127751564