当前位置:   article > 正文

<数据结构> C语言实现 单链表_c语言给链表赋值

c语言给链表赋值

目录

1.链表的概念及结构

2.实现单链表

2.1 SList.h

2.2 SList.c

2.2.1 单链表打印

Text.c进行测试

2.2.2单链表尾插

2.2.3 单链表头插

2.2.4单链表尾删

2.2.5单链表头删

2.2.6单链表查找

2.2.7单链表在pos位置之前插入

2.2.8单链表在pos位置之后插入

2.2.9单链表删除pos位置


1.链表的概念及结构

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

从上图可以看出:

1.链式结构在逻辑上时连续的,但在物理上不一定连续

2.显示中的节点一般都是从堆上申请出来的

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

2.实现单链表

2.1 SList.h

  1. #pragma once
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <assert.h>
  6. typedef int SLTDataType;
  7. typedef struct SListNode
  8. {
  9. SLTDataType data;
  10. struct SListNode* next;
  11. }SListNode;
  12. //单链表打印
  13. void SListPrint(SListNode* phead);
  14. //单链表销毁
  15. void SListDestory(SListNode** pphead);
  16. //单链表尾插
  17. void SListPushBack(SListNode** pphead, SLTDataType x);
  18. //单链表头插
  19. void SListPushFront(SListNode** pphead, SLTDataType x);
  20. //单链表尾删
  21. void SListPopBack(SListNode** pphead);
  22. //单链表头删
  23. void SListPopFront(SListNode** pphead);
  24. //找到x数据元素的位置
  25. SListNode* SListFind(SListNode* phead, SLTDataType x);
  26. //在pos位置之前插入x
  27. void SListInsertBefore(SListNode** pphead, SListNode* pos, SLTDataType x);
  28. //在pos位置之后插入x
  29. void SListInsertAfter(SListNode* pos, SLTDataType x);
  30. //删除pos位置的数据
  31. void SListErase(SListNode** pphead, SListNode* pos);

2.2 SList.c

2.2.1 单链表打印

 

 

  1. //单链表打印
  2. void SListPrint(SListNode* phead)
  3. {
  4. //创建新指针cur,并用phead赋值
  5. SListNode* cur = phead;
  6. //当cur为NULL时循环结束
  7. while (cur)
  8. {
  9. printf("%d->", cur->data);
  10. //将cur下一个结点赋值给cur
  11. cur = cur->next;
  12. }
  13. printf("NULL\n");
  14. }

Text.c进行测试

  1. #include "SList.h"
  2. int main()
  3. {
  4. //创建头节点
  5. SListNode* slist;
  6. //创建结点
  7. SListNode* n1 = (SListNode*)malloc(sizeof(SListNode));
  8. SListNode* n2 = (SListNode*)malloc(sizeof(SListNode));
  9. SListNode* n3 = (SListNode*)malloc(sizeof(SListNode));
  10. n1->data = 1;
  11. n2->data = 2;
  12. n3->data = 3;
  13. n1->next = n2;
  14. n2->next = n3;
  15. n3->next = NULL;
  16. //将n1赋值给头节点
  17. slist = n1;
  18. SListPrint(slist);
  19. return 0;
  20. }

结果:

2.2.2单链表尾插

由于头插、尾插等都需要建立新结点,所以将结点功能作为独立函数,方便后续调用,避免函数之间的冗余。

  1. //创建新节点
  2. SListNode* BuySListNode(SLDataType x)
  3. {
  4. SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
  5. if (newnode == NULL)
  6. {
  7. printf("malloc fail\n");
  8. exit(-1);
  9. }
  10. else
  11. {
  12. newnode->data = x;
  13. newnode->next = NULL;
  14. }
  15. }

 

1.若phead指向的头节点为空,则要将创建的新节点赋值给phead

             

 

 

2.若phead指向的头节点不为空,则要找尾(tail),并将新节点赋值给tail->next

 

 

  1. //单链表尾插
  2. void SListPushBack(SListNode* phead, SLDataType x)
  3. {
  4. SListNode* newnode = BuySListNode(x);
  5. if (phead == NULL)
  6. {
  7. phead = newnode;
  8. }
  9. else
  10. {
  11. //找尾
  12. SListNode* tail = phead;
  13. while (tail->next)
  14. {
  15. tail = tail->next;
  16. }
  17. tail->next = newnode;
  18. }
  19. }

 既然尾插函数写完了,那么让我们来进行测试。

Test.c

 

 我们进行了4次尾插,原本期待的结果是 0->1->2->3->NULL,但是运行出来的结果却是NULL。

那么是什么错误导致结果与预想有出入呢?

这个错误涉及到函数的传值传址。

我们将头结点slist传给SListNode* phead 实际上只是传了值。phead作为形参改变,不影响到实参。当链表为空时,我们将新结点赋值给phead影响不了原来的slist,那么后续操作也就都无效了。

解决方法:进行传址,将slist的地址传给SListNode** pphead,对pphead进行操作。

 

  1. //单链表尾插 correct
  2. void SListPushBack(SListNode** pphead, SLDataType x)
  3. {
  4. SListNode* newnode = BuySListNode(x);
  5. if (*pphead == NULL)
  6. {
  7. *pphead = newnode;
  8. }
  9. else
  10. {
  11. //找尾
  12. SListNode* tail = *pphead;
  13. while (tail->next)
  14. {
  15. tail = tail->next;
  16. }
  17. tail->next = newnode;
  18. }
  19. }

 

 

2.2.3 单链表头插

 

 

  1. //单链表头插
  2. void SListPushFront(SListNode** pphead, SLDataType x)
  3. {
  4. SListNode* newnode = BuySListNode(x);
  5. newnode->next = *pphead;
  6. *pphead = newnode;
  7. }

 

2.2.4单链表尾删

 

尾删分为三种情况:

1.当链表为空的时候,直接返回 return

2.当链表只有一个节点的时候,直接释放并置空头节点

3.当链表有两个以上节点的时候,用tail遍历链表,当tail->next->next ==NULL时,遍历结束并将tail->next释放置空。

 

  1. //单链表尾删
  2. void SListPopBack(SListNode** pphead)
  3. {
  4. if (*pphead == NULL)
  5. {
  6. return;
  7. }
  8. else if ((*pphead)->next == NULL)
  9. {
  10. free(*pphead);
  11. *pphead = NULL;
  12. }
  13. else
  14. {
  15. SListNode* tail = *pphead;
  16. while (tail->next->next)
  17. {
  18. tail = tail->next;
  19. }
  20. free(tail->next);
  21. tail->next = NULL;
  22. }
  23. }

2.2.5单链表头删

单链表头删分两种情况:

1.当单链表为空时,直接return

2.当单链表不为空时,将头指针置空,并将头指针next赋值给头指针

  1. //单链表头删
  2. void SListPopFront(SListNode** pphead)
  3. {
  4. if (*pphead == NULL)
  5. {
  6. return;
  7. }
  8. else
  9. {
  10. SListNode* next = (*pphead)->next;
  11. free(*pphead);
  12. *pphead = next;
  13. }
  14. }

2.2.6单链表查找

  1. //单链表查找
  2. SListNode* SListFind(SListNode* phead, SLDataType x)
  3. {
  4. SListNode* cur = phead;
  5. while (cur)
  6. {
  7. if (cur->data == x)
  8. {
  9. return cur;
  10. }
  11. cur = cur->next;
  12. }
  13. return NULL;
  14. }

2.2.7单链表在pos位置之前插入

分为两种情况:

1.当pos位置为头节点时,使用头插函数。

2.当pos为头节点之外的其它位置时,用prev->next遍历链表找到pos,创建新节点插入到prev和prev->next之间。

 

 

 

  1. //在pos位置之前插入x
  2. void SListInsertBefore(SListNode** pphead, SListNode* pos, SLDataType x)
  3. {
  4. assert(pphead);
  5. assert(pos);
  6. if (pos == *pphead)
  7. {
  8. SListNodeFront(pphead, x);
  9. }
  10. else
  11. {
  12. SListNode* prev = *pphead;
  13. while (prev->next != pos)
  14. {
  15. prev = prev->next;
  16. }
  17. SListNode* newnode = BuySListNode(x);
  18. prev->next = newnode;
  19. newnode->next = pos;
  20. }
  21. }

2.2.8单链表在pos位置之后插入

  1. //在pos位置之后插入x
  2. void SListInsertAfter(SListNode* pos, SLDataType x)
  3. {
  4. assert(pos);
  5. SListNode* newnode = BuySListNode(x);
  6. newnode->next = pos->next;
  7. pos->next = newnode;
  8. }

2.2.9单链表删除pos位置

分为两种情况:

1.当pos为头节点时,调用头删。

2.当pos不为头节点时,用prev->next指向pos->next,释放并置空pos

 

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

小结:单链表结构适合头插,尾插或任意插入不适合。

           如果要使用链表单独存储数据,后续学习的双向链表更加合适

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

闽ICP备14008679号