当前位置:   article > 正文

C语言数据结构二:单向链表_c语言linklist后面是什么

c语言linklist后面是什么

单项链表由两个结构体组成。

一、链表结点的结构体

首先是链表的结构体。链表的结构体有两部分构成:数据域和指针

数据域:之前我们会写具体类型,但是在数据结构中为了让类型泛指,所以我们也是void*类型。节点上只存地址。类型由使用者指向来确定。

指针域:指向下一个结点。

  1. struct LinkNode {
  2. void* data;
  3. struct LinkNode* next;
  4. };

这个链表数据结构如上。

之前写的链表初始化函数,有个不好的地方,只返回链表头结点。

拿到链表头结点,就相当于拿到了整个链表。

这样有个问题:如果链表一千多个结点,那么每次取值都要把前面一千多个取出来,才轮到要取的值。每次统计个数,也要数这么多。

所以我们加第二个结构体。

小结数据域更新为void*类型


二、第二个结构体:链表的结构体

这个结构体的成员,我们首先写上链表的头结点,相当于拿到了整个链表。

一般链表常见的操作是尾插,为了便于我们尾插,我们可以在链表结构体中,再定义一个尾结点。这张插入值时,只要在尾结点后面加上一个值即可,然后更新尾结点。不用再从头跑过来。

所以这第二个结构体,是需要结合我们使用情况来自定义的。如果我们的场景需要频繁尾插,就加上某项信息(属性)。

第二个结构体,更像是一个对链表的信息统计和管理

  1. // 链表数据类型
  2. struct LinkList {
  3. struct LinkNode header; // 如果写指针,需要手动开辟内存,拿到它。
  4. int size;
  5. //struct LinkNode Tail;
  6. };


三、头文件

  1. #pragma once
  2. // 宏定义,保证C程序在C++中也可以运行。
  3. #ifdef __cplusplus
  4. extern "C" {
  5. #endif
  6. // 链表结点数据类型
  7. struct LinkNode {
  8. void* data;
  9. struct LinkNode *next;
  10. };
  11. // 链表数据类型
  12. struct LinkList {
  13. struct LinkNode header; // 如果写指针,需要手动开辟内存,拿到它。
  14. int size;
  15. //struct LinkNode Tail;
  16. };
  17. // 初始化链表:以前返回链表,现在不再返回链表,不希望用户修改链表信息,做个隐藏。
  18. // 我们定义一个新类型:
  19. typedef void* LinkListll;
  20. LinkListll Init_LinkList();
  21. // 插入链表
  22. LinkListll Insert_LinkList(LinkListll list, int pos, void* data);
  23. // 遍历链表
  24. typedef void(*FOREACH)(void*);// 定义一个回调函数。
  25. void Foreach_LinkList(ListListll list, FOREACH myforeach);
  26. #ifdef __cplusplus
  27. }
  28. #endif

当然了,如果为了进一步隐藏结构体,我们可以把结构体放在.c文件中,这样就可以实现对数据的隐藏。

注意我们初始化定义了输出了一个void*类型。所以后面所有调用这个list时,函数内部,必须强制类型转换



四、接口实现

  1. #include"LinkList.h"
  2. //链表节点数据类型
  3. struct LinkNode
  4. {
  5. void *data;
  6. struct LinkNode *next;
  7. };
  8. //链表数据类型
  9. struct LList
  10. {
  11. struct LinkNode header;
  12. int size;
  13. };
  14. //初始化链表
  15. LinkList Init_LinkList()
  16. {
  17. struct LList *list = malloc(sizeof(struct LList));
  18. if (NULL == list)
  19. {
  20. return NULL;
  21. }
  22. list->header.data = NULL;
  23. list->header.next = NULL;
  24. list->size = 0;
  25. return list;
  26. }
  27. //插入节点
  28. void Insert_LinkList(LinkList list, int pos, void *data)
  29. {
  30. if (NULL == list)
  31. {
  32. return;
  33. }
  34. if (NULL == data)
  35. {
  36. return;
  37. }
  38. struct LList * mylist = (struct LList *)list;
  39. if (pos < 0 || pos > mylist->size)
  40. {
  41. pos = mylist->size;
  42. }
  43. //查找插入位置
  44. struct LinkNode *pCurrent = &(mylist->header);
  45. for (int i = 0; i < pos; ++i)
  46. {
  47. pCurrent = pCurrent->next;
  48. }
  49. //创建新节点
  50. struct LinkNode *newnode = malloc(sizeof(struct LinkNode));
  51. newnode->data = data;
  52. newnode->next = NULL;
  53. //新节点插入到链表中
  54. newnode->next = pCurrent->next;
  55. pCurrent->next = newnode;
  56. mylist->size++;
  57. }
  58. //遍历链表
  59. void Foreach_LinkList(LinkList list, FOREACH myforeach) /*回调函数*/
  60. {
  61. if (NULL == list)
  62. {
  63. return;
  64. }
  65. if (NULL == myforeach)
  66. {
  67. return;
  68. }
  69. struct LList * mylist = (struct LList *)list;
  70. struct LinkNode *pCurrent = mylist->header.next;
  71. while (pCurrent != NULL)
  72. {
  73. myforeach(pCurrent->data);
  74. pCurrent = pCurrent->next;
  75. }
  76. }
  77. //按位置删除
  78. void RemoveByPos_LinkList(LinkList list, int pos)
  79. {
  80. if (NULL == list)
  81. {
  82. return;
  83. }
  84. struct LList *mylist = (struct LList *)list;
  85. if (pos < 0 || pos > mylist->size - 1)
  86. {
  87. return;
  88. }
  89. //找位置
  90. struct LinkNode *pCurrent = &(mylist->header);
  91. for (int i = 0; i < pos; ++i)
  92. {
  93. pCurrent = pCurrent->next;
  94. }
  95. //先保存待删除结点
  96. struct LinkNode *pDel = pCurrent->next;
  97. //重新建立待删除结点的前驱和后继结点关系
  98. pCurrent->next = pDel->next;
  99. //释放删除节点内存
  100. free(pDel);
  101. pDel = NULL;
  102. mylist->size--;
  103. }
  104. //按值删除
  105. void RemoveByVal_LinkList(LinkList list, void *data, COMPARE compare)
  106. {
  107. if (NULL == list)
  108. {
  109. return;
  110. }
  111. if (NULL == data)
  112. {
  113. return;
  114. }
  115. if (NULL == compare)
  116. {
  117. return;
  118. }
  119. struct LList *mylist = (struct LList *)list;
  120. //辅助指针变量
  121. struct LinkNode *pPrev = &(mylist->header);
  122. struct LinkNode *pCurrent = pPrev->next;
  123. while (pCurrent != NULL)
  124. {
  125. if (compare(pCurrent->data, data))
  126. {
  127. //找到了
  128. pPrev->next = pCurrent->next;
  129. //释放删除节点内存
  130. free(pCurrent);
  131. pCurrent = NULL;
  132. mylist->size--;
  133. break;
  134. }
  135. pPrev = pCurrent;
  136. pCurrent = pCurrent->next;
  137. }
  138. }
  139. //清空链表
  140. void Clear_LinkList(LinkList list)
  141. {
  142. if (NULL == list)
  143. {
  144. return;
  145. }
  146. struct LList *mylist = (struct LList *)list;
  147. //辅助指针变量
  148. struct LinkNode *pCurrent = mylist->header.next;
  149. while (pCurrent != NULL)
  150. {
  151. //先缓存下一个节点的地址
  152. struct LinkNode *pNext = pCurrent->next;
  153. //释放当前结点内存
  154. free(pCurrent);
  155. pCurrent = pNext;
  156. }
  157. mylist->header.next = NULL;
  158. mylist->size = 0;
  159. }
  160. //大小
  161. int Size_LinkList(LinkList list)
  162. {
  163. if (NULL == list)
  164. {
  165. return -1;
  166. }
  167. struct LList *mylist = (struct LList *)list;
  168. return mylist->size;
  169. }
  170. //销毁链表
  171. void Destroy_LinkList(LinkList list)
  172. {
  173. if (NULL == list)
  174. {
  175. return;
  176. }
  177. //清空链表
  178. Clear_LinkList(list);
  179. free(list);
  180. list = NULL;
  181. }

注意遍历时需要用户输入回调函数;

注意按值删除时,需要用户输入值对比函数myCompare。因为到底如何对比,我们在设计时是不知道的,但是我们知道这边必须输入一个比较的函数。


五、链表的更高级用法

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<stdlib.h>
  5. struct LinkNode
  6. {
  7. struct LinkNode *next;
  8. };
  9. struct Person
  10. {
  11. struct LinkNode node;
  12. char name[64];
  13. int age;
  14. };

类似于类的继承关系。

单向链表如上,如果是双向链表,那么在LinkNode中再加一个自身结构体指针就可以。

解释一句:这个LinkNode最好放在结构体的前面,这样的结构便于查找地址,如果放在后面,就增加了计算偏移量的操作。也就是说:node属性在name和age之前。

最重要的是:注意类型的转换。

  1. void test()
  2. {
  3. struct Person p1 = { NULL, "aaa", 10 };
  4. struct Person p2 = { NULL, "bbb", 20 };
  5. struct Person p3 = { NULL, "ccc", 30 };
  6. struct Person p4 = { NULL, "ddd", 40 };
  7. struct Person p5 = { NULL, "eee", 50 };
  8. //这种指针类型转换一定要看懂。
  9. struct LinkNode *pp1 = (struct LinkNode *)&p1;
  10. struct LinkNode *pp2 = (struct LinkNode *)&p2;
  11. struct LinkNode *pp3 = (struct LinkNode *)&p3;
  12. struct LinkNode *pp4 = (struct LinkNode *)&p4;
  13. struct LinkNode *pp5 = (struct LinkNode *)&p5;
  14. // 这种转成指针域指针之后,无法访问到数据域的内容。
  15. pp1->next = pp2;
  16. pp2->next = pp3;
  17. pp3->next = pp4;
  18. pp4->next = pp5;
  19. pp5->next = NULL;
  20. struct LinkNode *pCurrent = pp1;
  21. while (pCurrent != NULL)
  22. {
  23. // 再将类型转换回来。变成数据域指针。
  24. struct Person *person = (struct Person *)pCurrent;
  25. printf("Name:%s Age:%d\n",person->name,person->age);
  26. pCurrent = pCurrent->next;
  27. }
  28. }
  29. int main(){
  30. test();
  31. system("pause");
  32. return EXIT_SUCCESS;
  33. }

将一种结构体数据域指针类型的指针,转换为另一种结构体指针域类型的指针。这样就可以保证转换之后,使用者访问不到数据域内容,进而只操作指针域内容。完成链接。

如果需要访问数据域内容,就需要再次转换回来。

指针类型可以实现互相转换呢。

完整写法:

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include<stdio.h>
  3. #include<string.h>
  4. #include<stdlib.h>
  5. //链表节点数据结构
  6. struct LinkNode
  7. {
  8. struct LinkNode *next;
  9. };
  10. //链表结构体
  11. struct LList
  12. {
  13. struct LinkNode header; //头结点
  14. int size;
  15. };
  16. typedef void * LinkList;
  17. //初始化链表
  18. LinkList Init_LinkList()
  19. {
  20. struct LList *list = malloc(sizeof(struct LList));
  21. if (NULL == list)
  22. {
  23. return NULL;
  24. }
  25. list->header.next = NULL;
  26. list->size = 0;
  27. return list;
  28. }
  29. void Insert_LinkList(LinkList list, int position, void *data)
  30. {
  31. if (NULL == list)
  32. {
  33. return;
  34. }
  35. if (NULL == data)
  36. {
  37. return;
  38. }
  39. struct LList * mylist = (struct LList *)list;
  40. struct LinkNode *mynode = (struct LinkNode *)data;
  41. if (position < 0 || position > mylist->size)
  42. {
  43. position = mylist->size;
  44. }
  45. //找位置(找到position位置的前一个位置)
  46. struct LinkNode *pCurrent = &(mylist->header);
  47. for (int i = 0; i < position; ++i)
  48. {
  49. pCurrent = pCurrent->next;
  50. }
  51. //数据入链表
  52. mynode->next = pCurrent->next;
  53. pCurrent->next = mynode;
  54. mylist->size++;
  55. }
  56. void Foreach_LinkList(LinkList list, void(*myforeach)(void *) )
  57. {
  58. if (NULL == list)
  59. {
  60. return;
  61. }
  62. if (NULL == myforeach)
  63. {
  64. return;
  65. }
  66. struct LList * mylist = (struct LList *)list;
  67. struct LinkNode *pCurrent = mylist->header.next;
  68. while (pCurrent != NULL)
  69. {
  70. struct LinkNode *pNext = pCurrent->next;
  71. myforeach(pCurrent);
  72. pCurrent = pNext;
  73. }
  74. }
  75. //删除节点
  76. void RemoveByPos_LinkList(LinkList list, int position)
  77. {
  78. if (NULL == list)
  79. {
  80. return;
  81. }
  82. struct LList * mylist = (struct LList *)list;
  83. if (position < 0 || position > mylist->size - 1)
  84. {
  85. return;
  86. }
  87. //辅助指针
  88. struct LinkNode *pCurrent = &(mylist->header);
  89. for (int i = 0; i < position; ++i)
  90. {
  91. pCurrent = pCurrent->next;
  92. }
  93. //缓存下待删除节点
  94. struct LinkNode *pDel = pCurrent->next;
  95. //重新建立待删除节点的前驱和后继结点关系
  96. pCurrent->next = pDel->next;
  97. mylist->size--;
  98. }
  99. void Destroy_LinkList(LinkList list)
  100. {
  101. if (NULL == list)
  102. {
  103. return;
  104. }
  105. free(list);
  106. list = NULL;
  107. }
  108. struct Person
  109. {
  110. struct LinkNode node; //结构体应该指针?
  111. char name[64];
  112. int age;
  113. };
  114. void myPrint(void *data)
  115. {
  116. struct Person *person = (struct Person *)data;
  117. printf("Name:%s Age:%d\n", person->name,person->age);
  118. }
  119. void test()
  120. {
  121. //初始化链表
  122. LinkList list = Init_LinkList();
  123. //创建数据
  124. struct Person p1 = { NULL, "aaa", 10 };
  125. struct Person p2 = { NULL, "bbb", 20 };
  126. struct Person p3 = { NULL, "ccc", 30 };
  127. struct Person p4 = { NULL, "ddd", 40 };
  128. struct Person p5 = { NULL, "eee", 50 };
  129. struct Person p6 = { NULL, "fff", 60 };
  130. //插入数据
  131. Insert_LinkList(list, 0, &p1);
  132. Insert_LinkList(list, 0, &p2);
  133. Insert_LinkList(list, 0, &p3);
  134. Insert_LinkList(list, 0, &p4);
  135. Insert_LinkList(list, 0, &p5);
  136. Insert_LinkList(list, 0, &p6);
  137. //遍历
  138. Foreach_LinkList(list, myPrint);
  139. //删除
  140. RemoveByPos_LinkList(list, 3);
  141. printf("------------------\n");
  142. //遍历
  143. Foreach_LinkList(list, myPrint);
  144. //销毁
  145. Destroy_LinkList(list);
  146. }
  147. int main(){
  148. test();
  149. system("pause");
  150. return EXIT_SUCCESS;
  151. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Guff_9hys/article/detail/859780
推荐阅读
相关标签
  

闽ICP备14008679号