数据结构之单链表、双向链表和循环链表

快来打我* 2022-12-14 13:33 301阅读 0赞

链表是一种最为基础的数据结构,需要动态开辟空间,在执行删除或插入操作时效率高。主要分类有:

  • 单链表
  • 双向链表
  • 循环链表
    链表的应用场景:(不需要预先知道数据规模;适应于频繁的删除插入操作。),如:
    内存管理:内存管理是操作系统中一项重要的任务,操作系统必须决定如何为系统中运行的进程分配和回收存储空间。链表能够用来跟踪可分配的内存片段信息。
    其他的数据结构:栈、队列、集合、哈希表和图都要依赖于链表。

单链表

简称链表,由各个数据成员通过一个指针彼此链接起来而组成。每个元素包括两部分:数据成员和一个称为next的指针。只能以一个方向进行遍历,从头到尾。
在这里插入图片描述

  1. //list.h
  2. #pragma once
  3. #ifndef LIST_H
  4. #define LIST_H
  5. #endif // !LIST_H
  6. #include<stdlib.h>
  7. //定义链表元素的结构
  8. typedef struct ListElmt_ {
  9. void * data;
  10. struct ListElmt_ *next;
  11. } ListElmt;
  12. //定义链表的结构
  13. typedef struct List_ {
  14. //size表示链表元素的个数
  15. int size;
  16. //match并不是由链表本身使用,而是由从链表数据结构派生而来的新类型所使用。
  17. int(*match)(const void *key1, const void *key2);
  18. //destroy是封装之后传递给list_init的析构函数
  19. void(*destroy)(void *data);
  20. //指向头结点的指针
  21. ListElmt *head;
  22. //指向尾结点的指针
  23. ListElmt *tail;
  24. }List;
  25. //公共接口
  26. //初始化
  27. void list_init(List *list, void(*destroy)(void *data));
  28. //销毁
  29. void list_destroy(List *list);
  30. //如果插入元素成功则返回0,否则返回-1
  31. /*
  32. 在list指定的链表中element后面插入一个新元素,如果element为空,说明链表为空,新元素插入链表头部
  33. 新元素包含一个指向data的指针,只要该元素在链表中,data所引用的内存空间要保持合法
  34. */
  35. int list_ins_next(List *list, ListElmt *element, const void* data);
  36. /*移除element后的一个元素,data指向已移除中存储的元素
  37. 如果element为NULL,则移除头结点*/
  38. int list_rem_next(List* list, ListElmt * element, void **data);
  39. //int list_size(const List* list);
  40. //
  41. 返回指向链表中头元素的指针
  42. //ListElmt * list_head(const List *list);
  43. 返回指向链表尾元素的指针
  44. //ListElmt * list_tail(const List *list);
  45. 判断是否头结点,尾结点
  46. //int list_is_head(const ListElmt *element);
  47. //int list_is_tail(const ListElmt *element);
  48. //
  49. 返回节点中保存的数据
  50. //void *list_data(const List *element);
  51. #define list_size(list) ((list)->size)
  52. #define list_head(list) ((list)->head)
  53. #define list_tail(list) ((list)->tail)
  54. #define list_is_head(list,element) ((element) == (list)->head?1:0)
  55. #define list_is_tail(element) ((element)->next == NULL ? 1:0)
  56. #define list_data(element) ((element)->data)
  57. #define list_next(element) ((element)->next)
  58. //list.c
  59. #include<stdlib.h>
  60. #include<string.h>
  61. #include"list.h"
  62. //初始化链表
  63. void list_init(List *list, void(*destroy)(void *data)) {
  64. list->size = 0;
  65. list->destroy = destroy;
  66. list->head = NULL;
  67. list->tail = NULL;
  68. return;
  69. }
  70. //销毁链表
  71. void list_destroy(List *list) {
  72. void * data;
  73. //移除每个元素
  74. while (list_size(list) > 0)
  75. {
  76. if (list_rem_next(list, NULL, (void **)&data) == 0 && list->destroy != NULL) {
  77. //调用一个用户分配的函数来释放动态分配的内存
  78. list->destroy(data);
  79. }
  80. }
  81. //现在无操作被允许,但是要对这个结构进行清零操作,作为预防措施
  82. memset(list, 0, sizeof(List));
  83. return;
  84. }
  85. //移除element之后的一个元素
  86. int list_rem_next(List *list, ListElmt *element, void **data) {
  87. ListElmt *old_element;
  88. //如果链表为空,则直接返回-1;
  89. if (list_size(list) == 0) {
  90. return -1;
  91. }
  92. //如果element为空,删除头结点
  93. if (element == NULL) {
  94. *data = list->head->data;
  95. old_element = list->head;
  96. list->head = list->head->next;
  97. if (list_size(list) == 1) {
  98. list->tail = NULL;
  99. }
  100. }
  101. else
  102. {
  103. //需要删除的元素在头结点之外
  104. //如果删除的元素为空,则直接返回
  105. if (element->next == NULL) {
  106. return -1;
  107. }
  108. *data = element->next->data;
  109. old_element = element->next;
  110. element->next = element->next->next;
  111. //如果删除外element后的节点后,element之后没节点了,说明element成了尾结点
  112. if (element->next == NULL) {
  113. list->tail = element;
  114. }
  115. }
  116. //old_element是需要删除的节点,释放他的内存
  117. free(old_element);
  118. //调整list的size
  119. list->size--;
  120. return 0;
  121. }
  122. //在element之后插入一个元素
  123. int list_ins_next(List *list, ListElmt *element, const void *data) {
  124. ListElmt * new_element;
  125. //为该元素分配内存
  126. new_element = (ListElmt*)malloc(sizeof(ListElmt));
  127. //如果未分配到内存
  128. if (new_element == NULL) return -1;
  129. //需要强制转换,const类型不能转给非const类型
  130. new_element->data =(void *) data;
  131. //如果element为空,则把new_element作为头结点
  132. if (element == NULL) {
  133. //如果是个空链表
  134. if (list_size(list) == 0) {
  135. list->tail = new_element;
  136. }
  137. new_element->next = list->head;
  138. list->head = new_element;
  139. }
  140. else
  141. {
  142. //如果element为尾结点
  143. if (element->next == NULL) {
  144. list->tail = new_element;
  145. }
  146. new_element->next = element->next;
  147. element->next = new_element;
  148. }
  149. list->size++;
  150. return 0;
  151. }

双向链表

链表元素由两个指针链接。每个元素有三部分组成:除了数据成员和next指针外,还有包含一个指向前一个元素的指针prev。为了标识头和尾,将第一个元素的prev指针和最后一个元素的next指针置为NULL。它可以从头到尾,也可以从尾到头遍历整个链表。
在这里插入图片描述

  1. //dlist.h
  2. #pragma once
  3. #ifndef DLIST_H
  4. #define DLIST_H
  5. #include<stdlib.h>
  6. typedef struct DListElmt_ {
  7. void *data;
  8. struct DListElmt_ *prev;
  9. struct DListElmt_ *next;
  10. } DListElmt;
  11. typedef struct DList_ {
  12. int size;
  13. int(*match)(const void *key1, const void *key2);
  14. void(*destroy)(void *data);
  15. DListElmt *head;
  16. DListElmt *tail;
  17. } DList;
  18. //公共接口
  19. void dlist_init(DList * list, void(*destroy)(void *data));
  20. void dlist_destroy(DList *list);
  21. int dlist_ins_next(DList *list, DListElmt * element, const void *data);
  22. int dlist_ins_prev(DList *list, DListElmt * element, const void *data);
  23. int dlist_remove(DList *list, DListElmt *element, void **data);
  24. #define dlist_size(list) ((list)->size)
  25. #define dlist_head(list) ((list)->head)
  26. #define dlist_tail(list) ((list)->tail)
  27. #define dlist_is_head(element) ((element)->prev == NULL ? 1:0)
  28. #define dlist_is_tail(element) ((element)->next == NULL ? 1:0)
  29. #define dlist_data(element) ((element)->data)
  30. #define dlist_next(element) ((element)->next)
  31. #define dlist_prev(element) ((element)->prev)
  32. #endif // !DLIST_H
  33. //dlist.c
  34. #include<stdlib.h>
  35. #include<string.h>
  36. #include"dlist.h"
  37. void dlist_init(DList *list, void(*destroy)(void *data)) {
  38. list->size = 0;
  39. list->destroy = destroy;
  40. list->head = NULL;
  41. list->tail = NULL;
  42. return;
  43. }
  44. void dlist_destroy(DList *list) {
  45. void *data;
  46. while (dlist_size(list)>0)
  47. {
  48. if (dlist_remove(list, dlist_tail(list), (void **)&data) == 0 && list->destroy != NULL) {
  49. //调用用户自定义的函数来释放动态分配的数据data
  50. list->destroy(data);
  51. }
  52. }
  53. memset(list, 0, sizeof(list));
  54. return;
  55. }
  56. //将元素插入element之后
  57. int dlist_ins_next(DList *list, DListElmt *element, const void *data) {
  58. DListElmt * new_element;
  59. //不可能的情况
  60. if (element == NULL && dlist_size(list) != 0) {
  61. return -1;
  62. }
  63. //为要插入的元素分配内存,内存分配失败返回-1
  64. if ((new_element = (DListElmt*)malloc(sizeof(DListElmt))) == NULL) {
  65. return -1;
  66. }
  67. new_element->data =(void *) data;
  68. //如果链表为空,该元素要作为头结点
  69. if (dlist_size(list) == 0) {
  70. new_element->prev = NULL;
  71. new_element->next = NULL;
  72. list->head = new_element;
  73. list->tail = new_element;
  74. }
  75. else {
  76. new_element->prev = element;
  77. new_element->next = element->next;
  78. element->next = new_element;
  79. //还要考虑element后为空的情况,插入之后,new_element就变成最后一个了
  80. if (element->next == NULL) {
  81. list->tail = new_element;
  82. }
  83. else {
  84. element->next->prev = new_element;
  85. }
  86. }
  87. list->size++;
  88. return 0;
  89. }
  90. //在element之前插入
  91. int dlist_ins_prev(DList *list, DListElmt *element, const void *data) {
  92. //先创建一个新元素
  93. DListElmt * new_element;
  94. if (element == NULL && dlist_size(list) == 0) {
  95. return -1;
  96. }
  97. //分配内存
  98. new_element = (DListElmt *)malloc(sizeof(DListElmt));
  99. if (new_element == NULL) {
  100. //分配内存失败
  101. return -1;
  102. }
  103. //将数据放入
  104. new_element->data = (void *)data;
  105. //如果element为NULL,说明链表为空,也可以用size==0表示,插入的元素为第一个
  106. if (element == NULL) {
  107. new_element->next = NULL;
  108. new_element->prev = NULL;
  109. list->head = new_element;
  110. list->tail = new_element;
  111. }
  112. else
  113. {
  114. new_element->next = element;
  115. new_element->prev = element->prev;
  116. //如果该元素是头元素,则新元素应该成为新的头元素
  117. if (element->prev == NULL) {
  118. list->head = new_element;
  119. }
  120. else {
  121. element->prev->next = new_element;
  122. }
  123. element->prev = new_element;
  124. }
  125. list->size++;
  126. return 0;
  127. }
  128. //删除元素
  129. int dlist_remove(DList *list, DListElmt * element, void ** data) {
  130. //如果元素为空,或者链表为空,删除失败
  131. if (element == NULL || dlist_size(list) == 0)
  132. return -1;
  133. *data = element->data;
  134. //如果该数据是头结点
  135. if (element->prev == NULL) {
  136. list->head = element->next;
  137. //如果链表只有一个元素
  138. if (element->next == NULL) {
  139. list->tail = NULL;
  140. }
  141. else {
  142. element->next->prev = NULL;
  143. }
  144. }
  145. else {
  146. element->prev->next = element->next;
  147. //如果这个元素是最后一个
  148. if (element->next == NULL) {
  149. list->tail = element->prev;
  150. }
  151. else {
  152. element->next->prev = element->prev;
  153. }
  154. }
  155. //释放内存
  156. free(element);
  157. list->size--;
  158. return 0;
  159. }

循环链表

在这里插入图片描述
在这里插入图片描述
循环链表可以是单向的也可以是双向的。区分一个链表是不是循环链表只要看它有没有尾部元素即可。
在循环链表中,最后一个元素指向头元素,而不是NULL;如果是双向的循环链表,它的头元素的prev要指向最后一个元素,而不是NULL。

  1. //clist.h
  2. #pragma once
  3. #ifndef CLIST_H
  4. #define CLIST_H
  5. #include<stdlib.h>
  6. typedef struct CListElmt_ {
  7. void *data;
  8. struct CListElmt_ *next;
  9. } CListElmt;
  10. typedef struct Clist_ {
  11. int size;
  12. int(*match)(const void *key1, const void *key2);
  13. void(*destroy)(void *data);
  14. CListElmt *head;
  15. } CList;
  16. //公共接口
  17. void clist_init(CList *list, void(*destroy)(void *data));
  18. void clist_destroy(CList *list);
  19. int clist_ins_next(CList *list, CListElmt *element, const void *data);
  20. int clist_rem_next(CList *list, CListElmt *element, void **data);
  21. #define clist_size(list) ((list)->size)
  22. #define clist_head(list) ((list)->head)
  23. #define clist_data(element) ((element)->data)
  24. #define clist_next(element) ((element)->next)
  25. #endif // !CLIST_H
  26. #include<stdlib.h>
  27. #include<string.h>
  28. #include"clist.h"
  29. void clist_init(CList *list, void(*destroy)(void *data)) {
  30. list->size = 0;
  31. list->head = NULL;
  32. list->destroy = destroy;
  33. return;
  34. }
  35. void clist_destroy(CList *list) {
  36. void *data;
  37. while (clist_size(list)>0)
  38. {
  39. if (clist_rem_next(list, list->head, (void **)&data) == 0 && list->destroy !=NULL) {
  40. list->destroy(data);
  41. }
  42. }
  43. memset(list, 0, sizeof(CList));
  44. return;
  45. }
  46. int clist_ins_next(CList *list, CListElmt *element, const void*data) {
  47. CListElmt * new_element;
  48. if ((new_element = (CListElmt *)malloc(sizeof(CListElmt))) == NULL) {
  49. return -1;
  50. }
  51. new_element->data = (void *)data;
  52. //如果链表为空
  53. if (clist_size(list) == 0) {
  54. list->head = new_element;
  55. list->head->next = new_element;
  56. }
  57. else {
  58. new_element->next = element->next;
  59. element->next = new_element;
  60. }
  61. list->size++;
  62. return 0;
  63. }
  64. int clist_rem_next(CList *list, CListElmt *element, void ** data) {
  65. CListElmt * old_element;
  66. if (clist_size(list) == 0) {
  67. return -1;
  68. }
  69. //如果只有一个元素
  70. if (clist_size(list) == 1) {
  71. list->head = NULL;
  72. old_element = element;
  73. }
  74. else {
  75. old_element = element;
  76. element->next = element->next->next;
  77. if (old_element == clist_head(list)) {
  78. list->head = old_element->next;
  79. }
  80. }
  81. free(old_element);
  82. list->size--;
  83. return 0;
  84. }

发表评论

表情:
评论列表 (有 0 条评论,301人围观)

还没有评论,来说两句吧...

相关阅读