小言_互联网的博客

【数据结构】单链表(线性表)的实现

326人阅读  评论(0)

目录

      一、什么是链表

      二、单链表的实现

            1、动态申请一个结点

            2、单链表打印

            3、单链表尾插

            4、单链表的尾删

            5、单链表的头插

            6、单链表头删

            7、单链表查找

            8、单链表在pos位置之后插入x

            9、单链表删除pos位置之后的值

            10、单链表在pos位置之前插入x

            11、单链表删除pos位置的值

            12、销毁链表

      三、源代码

            1、SList.h

            2、SList.c

            3、test.c


一、什么是链表

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

单链表的声明如下:


  
  1. //声明单链表
  2. typedef struct SListNode
  3. {
  4. SLTDataType data;
  5. struct SListNode* next;
  6. }SLTNode;

链表的逻辑结构:

链表的物理结构:

注意:

1、从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续。

2、现实中的结点一般都是从堆上申请出来的。

3、从堆上申请空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。

假设在32位系统上,结点中值域为int类型,则一个节点的大小为8个字节,则也可能有下述链表:

每个链表都是由一节节的链结组成的,我们称之为节点。其中,每一个节点都是由两部分组成的,存储数据的部分叫做数据域,存储地址的部分叫做指针域。指向第一个节点的指针称之为头指针

二、单链表的实现

  1、动态申请一个结点 

在链表中插入一个数据,首先需要先动态申请一个结点,并将该结点的数值域与指针域进行赋值,指针域都设置为NULL。


  
  1. //动态申请一个节点
  2. SLTNode* BuySLTNode(SLTDataType x)
  3. {
  4. SLTNode* newnode = (SLTNode*)malloc( sizeof(SLTNode));
  5. if (newnode == NULL)
  6. {
  7. perror( "malloc fail");
  8. exit( -1);
  9. }
  10. newnode->data = x;
  11. newnode->next = NULL;
  12. return newnode;
  13. }

  2、单链表打印

 在这里我的思路是用while循环将链表遍历一遍,从首个结点开始移动,直到NULL结束。


  
  1. //打印链表
  2. void SLTPprint(SLTNode* phead)
  3. {
  4. SLTNode* cur = phead;
  5. while (cur != NULL)
  6. {
  7. printf( "%d->", cur->data);
  8. cur = cur->next;
  9. }
  10. printf( "NULL\n");
  11. }

  3、单链表尾插

尾插就需要我们先动态开辟一个新的节点 。然后让前面的指针指向新的节点。但是要分两种情况如下图所示:

因为链表为NULL时,plist指针指向的是空,此时不能进行解引用操作,所以如果为NULL时,直接进行赋值就行。当链表不为空时,直接在后面进行尾插。 

注意:

在这里使用的是二级指针,因为需要改变结构体里面的数值,而头指针是一个结构体类型的指针,而指针也是一种变量。假设我们用的是一级指针,当链表为空,我们进行尾插的时候,我们会将头指针内的数据改为新节点的地址。我们可以找到,一级指针的传递方式不会引起实参的变化。因此,当此函数结束后,我们的头指针依旧是空。因此,我们这里需要传入的是二级指针,从而实现地址传递。


  
  1. //尾插
  2. void SLTPushBack(SLTNode** pphead, SLTDataType x)
  3. {
  4. SLTNode* newnode = BuySLTNode(x);
  5. if (*pphead == NULL)
  6. {
  7. *pphead = newnode;
  8. }
  9. else
  10. {
  11. SLTNode* tail = *pphead;
  12. //找尾
  13. while (tail->next)
  14. {
  15. tail = tail->next;
  16. }
  17. tail->next = newnode;
  18. }
  19. }

  4、单链表的尾删

在这里首先要要声明一下,保证链表不为空,然后再分两种情况:(1)如果链表长度大于1时,只需要将倒是第二个节点的指针域设置为空指针,并且将最后一个节点释放掉。(2)如果链表长度为1时,只需将头指针置空,第一个节点释放掉就行。


  
  1. //单链表尾删
  2. void SLTPopBack(SLTNode** pphead)
  3. {
  4. //暴力的检查
  5. assert(*pphead);
  6. if ((*pphead)->next == NULL)
  7. {
  8. free(*pphead);
  9. *pphead = NULL;
  10. }
  11. else
  12. {
  13. SLTNode* tail = *pphead;
  14. while (tail->next->next)
  15. {
  16. tail = tail->next;
  17. }
  18. free(tail->next);
  19. tail->next = NULL;
  20. }
  21. }

  5、单链表的头插

头插也是先要开辟一个新的节点,然后直接让新节点的next链接到头节点上,最后重新更新一下头节点。


  
  1. //头插
  2. void SLTPushFront(SLTNode** pphead, SLTDataType x)
  3. {
  4. SLTNode* newnode = BuySLTNode(x);
  5. newnode->next = *pphead;
  6. *pphead = newnode;
  7. }

  6、单链表头删

头删首先还是要声明一下,保证链表不为空,然后保存头指针指向指针域中所对的地址空间,最后释放头节点即可。


  
  1. //头删
  2. void SLTPopFront(SLTNode** pphead)
  3. {
  4. assert(*pphead);
  5. SLTNode* next = (*pphead)->next;
  6. free(*pphead);
  7. *pphead = next;
  8. }

  7、单链表查找

 直接用while循环将链表遍历一遍。注意返回值返回的是空指针或者所查元素的地址。


  
  1. //查找
  2. SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
  3. {
  4. SLTNode* cur = phead;
  5. while (cur)
  6. {
  7. if (cur->data == x)
  8. {
  9. return cur;
  10. }
  11. else
  12. cur = cur->next;
  13. }
  14. return NULL;
  15. }

  8、单链表在pos位置之后插入x

在pos位置之后插入x时,pos位置所对的节点的指针域中就记录了后面的节点位置。因此可以直接插入x所对应的节点。


  
  1. //在pos之后插入x
  2. void SLTInsertAfter(SLTNode* pos, SLTDataType x)
  3. {
  4. assert(pos);
  5. SLTNode* newnode = BuySLTNode(x);
  6. newnode->next = pos->next;
  7. pos->next = newnode;
  8. }

  9、单链表删除pos位置之后的值

在删除pos位置之后的节点时,如果pos->next为空时,说明pos位置就是最后一个节点,它的后面已经没有节点了,所以直接返回即可,如果pos->next不为空时,要保存住这个位置,然后重新链接pos->next = pos->next->next(就相当于下面代码中pos->next = nextNode->next),最后释放pos->next。


  
  1. //删除pos之后的值
  2. void SLTEraseAfter(SLTNode* pos)
  3. {
  4. assert(pos);
  5. if (pos->next == NULL)
  6. {
  7. return;
  8. }
  9. else
  10. {
  11. SLTNode* nextNode = pos->next;
  12. pos->next = nextNode->next;
  13. free(nextNode);
  14. }
  15. }

  10、单链表在pos位置之前插入x

在pos位置前面插入新的节点,分两种情况:(1)如果pos的位置就是头节点时,就相当于头插。(2)如果pos位置不是头节点时,要找到pos位置原链表的前方的节点。然后让该节点指向所插入的节点,然后让所插入的节点指向pos所对的节点。


  
  1. //在pos之前插入x
  2. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
  3. {
  4. assert(pos);
  5. if (*pphead == pos)
  6. {
  7. SLTPushFront(pphead, x);
  8. }
  9. else
  10. {
  11. SLTNode* prev = *pphead;
  12. while (prev->next != pos)
  13. {
  14. prev = prev->next;
  15. }
  16. SLTNode* newnode = BuySLTNode(x);
  17. prev->next = newnode;
  18. newnode->next = pos;
  19. }
  20. }

  11、单链表删除pos位置的值

删除pos位置的节点,分两种情况:(1)如果pos的位置就是头节点时,就相当于头删。(2)如果pos的位置不是头节点时,先保存pos位置后面的节点,然后让该节点链接pos位置之前的节点,最后再释放掉pos位置的节点。


  
  1. //删除pos位置的数据
  2. void SLTErase(SLTNode** pphead, SLTNode* pos)
  3. {
  4. assert(pos);
  5. if (pos == *pphead)
  6. {
  7. SLTPopFront(pphead);
  8. }
  9. else
  10. {
  11. SLTNode* prev = *pphead;
  12. while (prev->next != pos)
  13. {
  14. prev = prev->next;
  15. }
  16. prev->next = pos->next;
  17. free(pos);
  18. }
  19. }

  12、销毁链表

销毁链表的时候,我们不能简单只释放头指针。这样是没有将空间完全释放掉的,这只是释放了头节点。所以可以设置一个指针cur,让它用while循环将链表从头到尾都释放一遍,最后将头指针设置为空。一定要将头指针设置为空指针!否则会出现野指针的问题!


  
  1. //销毁
  2. void SLTDestroy(SLTNode** pphead)
  3. {
  4. SLTNode* cur = *pphead;
  5. while (cur)
  6. {
  7. SLTNode* next = cur->next;
  8. free(cur);
  9. cur = next;
  10. }
  11. *pphead = NULL;
  12. }

三、源代码

  1、SList.h


  
  1. #pragma once
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <assert.h>
  5. typedef int SLTDataType;
  6. //声明单链表
  7. typedef struct SListNode
  8. {
  9. SLTDataType data;
  10. struct SListNode* next;
  11. }SLTNode;
  12. //动态申请一个节点
  13. SLTNode* BuySLTNode(SLTDataType x);
  14. //创建循环节点
  15. SLTNode* CreateSList( int n);
  16. //打印链表
  17. void SLTPprint(SLTNode* phead);
  18. //查找
  19. SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
  20. //尾插尾删
  21. void SLTPushBack(SLTNode** pphead, SLTDataType x);
  22. void SLTPopBack(SLTNode** pphead);
  23. //头插头删
  24. void SLTPushFront(SLTNode** pphead, SLTDataType x);
  25. void SLTPopFront(SLTNode** pphead);
  26. //在pos之后插入x
  27. void SLTInsertAfter(SLTNode* pos, SLTDataType x);
  28. //删除pos之后的值
  29. void SLTEraseAfter(SLTNode* pos);
  30. //在pos之前插入x
  31. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
  32. //删除pos位置的数据
  33. void SLTErase(SLTNode** pphead, SLTNode* pos);
  34. //销毁
  35. void SLTDestroy(SLTNode** pphead);

  2、SList.c


  
  1. #include "SList.h"
  2. //动态申请一个节点
  3. SLTNode* BuySLTNode(SLTDataType x)
  4. {
  5. SLTNode* newnode = (SLTNode*)malloc( sizeof(SLTNode));
  6. if (newnode == NULL)
  7. {
  8. perror( "malloc fail");
  9. exit( -1);
  10. }
  11. newnode->data = x;
  12. newnode->next = NULL;
  13. return newnode;
  14. }
  15. //创建循环节点
  16. SLTNode* CreateSList( int n)
  17. {
  18. SLTNode* phead = NULL, * ptail = NULL;
  19. for ( int i = 0; i < n; i++)
  20. {
  21. SLTNode* newnode = BuySLTNode(i);
  22. if (phead == NULL)
  23. {
  24. ptail = phead = newnode;
  25. }
  26. else
  27. {
  28. ptail->next = newnode;
  29. ptail = newnode;
  30. }
  31. }
  32. return phead;
  33. }
  34. //打印链表
  35. void SLTPprint(SLTNode* phead)
  36. {
  37. SLTNode* cur = phead;
  38. while (cur != NULL)
  39. {
  40. //printf("[%d | %p]->", cur->data, cur->next);
  41. printf( "%d->", cur->data);
  42. cur = cur->next;
  43. }
  44. printf( "NULL\n");
  45. }
  46. //尾插
  47. void SLTPushBack(SLTNode** pphead, SLTDataType x)
  48. {
  49. SLTNode* newnode = BuySLTNode(x);
  50. if (*pphead == NULL)
  51. {
  52. *pphead = newnode;
  53. }
  54. else
  55. {
  56. SLTNode* tail = *pphead;
  57. //找尾
  58. while (tail->next)
  59. {
  60. tail = tail->next;
  61. }
  62. tail->next = newnode;
  63. }
  64. }
  65. void SLTPopBack(SLTNode** pphead)
  66. {
  67. //暴力的检查
  68. assert(*pphead);
  69. if ((*pphead)->next == NULL)
  70. {
  71. free(*pphead);
  72. *pphead = NULL;
  73. }
  74. else
  75. {
  76. SLTNode* tail = *pphead;
  77. while (tail->next->next)
  78. {
  79. tail = tail->next;
  80. }
  81. free(tail->next);
  82. tail->next = NULL;
  83. }
  84. }
  85. //头插
  86. void SLTPushFront(SLTNode** pphead, SLTDataType x)
  87. {
  88. SLTNode* newnode = BuySLTNode(x);
  89. newnode->next = *pphead;
  90. *pphead = newnode;
  91. }
  92. //头删
  93. void SLTPopFront(SLTNode** pphead)
  94. {
  95. assert(*pphead);
  96. SLTNode* next = (*pphead)->next;
  97. free(*pphead);
  98. *pphead = next;
  99. }
  100. //查找
  101. SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
  102. {
  103. SLTNode* cur = phead;
  104. while (cur)
  105. {
  106. if (cur->data == x)
  107. {
  108. return cur;
  109. }
  110. else
  111. cur = cur->next;
  112. }
  113. return NULL;
  114. }
  115. //在pos之后插入x
  116. void SLTInsertAfter(SLTNode* pos, SLTDataType x)
  117. {
  118. assert(pos);
  119. SLTNode* newnode = BuySLTNode(x);
  120. newnode->next = pos->next;
  121. pos->next = newnode;
  122. }
  123. //在pos之前插入x
  124. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
  125. {
  126. assert(pos);
  127. if (*pphead == pos)
  128. {
  129. SLTPushFront(pphead, x);
  130. }
  131. else
  132. {
  133. SLTNode* prev = *pphead;
  134. while (prev->next != pos)
  135. {
  136. prev = prev->next;
  137. }
  138. SLTNode* newnode = BuySLTNode(x);
  139. prev->next = newnode;
  140. newnode->next = pos;
  141. }
  142. }
  143. //删除pos之后的值
  144. void SLTEraseAfter(SLTNode* pos)
  145. {
  146. assert(pos);
  147. if (pos->next == NULL)
  148. {
  149. return;
  150. }
  151. else
  152. {
  153. SLTNode* nextNode = pos->next;
  154. pos->next = nextNode->next;
  155. free(nextNode);
  156. }
  157. }
  158. //删除pos位置的数据
  159. void SLTErase(SLTNode** pphead, SLTNode* pos)
  160. {
  161. assert(pos);
  162. if (pos == *pphead)
  163. {
  164. SLTPopFront(pphead);
  165. }
  166. else
  167. {
  168. SLTNode* prev = *pphead;
  169. while (prev->next != pos)
  170. {
  171. prev = prev->next;
  172. }
  173. prev->next = pos->next;
  174. free(pos);
  175. }
  176. }
  177. //销毁
  178. void SLTDestroy(SLTNode** pphead)
  179. {
  180. SLTNode* cur = *pphead;
  181. while (cur)
  182. {
  183. SLTNode* next = cur->next;
  184. free(cur);
  185. cur = next;
  186. }
  187. *pphead = NULL;
  188. }

  3、test.c


  
  1. TestSList1()
  2. {
  3. SLTNode* plist = NULL;
  4. SLTPushBack(&plist, 1);
  5. SLTPushBack(&plist, 2);
  6. SLTPushBack(&plist, 3);
  7. SLTPushBack(&plist, 4);
  8. SLTPushBack(&plist, 5);
  9. SLTPprint(plist);
  10. SLTPopBack(&plist);
  11. SLTPprint(plist);
  12. SLTPushFront(&plist, 100);
  13. SLTPushFront(&plist, 200);
  14. SLTPprint(plist);
  15. SLTPopFront(&plist);
  16. SLTPprint(plist);
  17. SLTNode* p = SLTFind(plist, 2);
  18. SLTInsertAfter(p, 30);
  19. SLTPprint(plist);
  20. p = SLTFind(plist, 1);
  21. SLTEraseAfter(p);
  22. SLTPprint(plist);
  23. SLTDestroy(&plist);
  24. }
  25. int main()
  26. {
  27. TestSList1();
  28. return 0;
  29. }


本文要是有不足的地方,欢迎大家在下面评论,我会在第一时间更正。

  老铁们,记着点赞加关注哦!!!


转载:https://blog.csdn.net/m0_63198468/article/details/128462123
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场