当前位置:   article > 正文

c语言实现链表(详解)_c语言链表实现

c语言链表实现

在上篇顺序表文章中,我们总结出了顺序表的一些缺点。

1.空间不够了需要增容,扩容有代价

2.为了减少扩容的代价,一般扩容扩大两倍,这样可能会导致浪费一定的空间

3.顺序表要求从头位置开始存储,如果我们从头部或者中间开始插入或者删除数据就要挪动数据,效率低

针对顺序表的缺陷,可以设计链表。

本文详细介绍简单的单链表的实现和多种接口函数

1.链表的定义

每个链表结点有两部分,第一部分存储这个结点的值。第二部分存储链表要连接的下一个结点的地址。

  1. typedef struct SListNode
  2. {
  3. SLDataType val;
  4. struct SListNode* next;
  5. }SL;

2.链表接口函数的shixian(增删查改)

2.1创建一个新的结点

  1. SL* CreatListNode(SLDataType x)
  2. {
  3. SL* newnode = (SL*)malloc(sizeof(SL));
  4. if (newnode == NULL)
  5. {
  6. printf("malloc失败");
  7. exit(-1);
  8. }
  9. newnode->val = x;
  10. newnode->next = NULL;
  11. return newnode;
  12. }

每次要增加新的结点的时候调用这个函数即可

2.2打印整个链表的值

为了便于观察接口函数的功能 ,我们需要打印链表来观察是否出现错误

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

2.3尾插数据

  1. //尾插数据
  2. //注意要使用二级指针,使用一级指针只是传值,不会改变实际参数的值
  3. void SlistPushback(SL** phead, SLDataType x)
  4. {
  5. //1.创建一个新的结点
  6. SL* newnode = CreatListNode(x);
  7. //2.将新的结点连接到链表的尾部
  8. if (*phead == NULL)
  9. *phead = newnode;
  10. else
  11. {
  12. SL* cur = *phead;
  13. while (cur->next != NULL)
  14. {
  15. cur = cur->next;
  16. }
  17. cur->next = newnode;
  18. }
  19. }

有一点要注意,由于我们没有哨兵结点,要插入数据需要传入结点的地址,故要使用二级指针。传入一级指针是传值。

2.4头插数据

  1. //头插数据
  2. void SlistPushFront(SL** pphead, SLDataType x)
  3. {
  4. //1.创建一个新的结点
  5. SL* newnode = CreatListNode(x);
  6. //2.头插数据
  7. newnode->next = *pphead;
  8. *pphead = newnode;
  9. }

头插结点比较简单,无论链表是否为空,让新的结点指向这个链表的头节点即可

然后让这个结点成为新的头

2.5尾删数据

  1. //尾删数据
  2. void SlistPopback(SL** pphead)
  3. {
  4. assert(*pphead != NULL);
  5. //1.链表中只有一个结点
  6. if ((*pphead)->next == NULL)
  7. {
  8. free(*pphead);
  9. *pphead = NULL;
  10. }
  11. else//有一个以上的结点
  12. {
  13. //1.找到尾节点并且删除这个结点
  14. SL* tail = *pphead;
  15. SL* prev = NULL;//用来记录当前尾结点的前一个结点,删除尾结点后让这个结点指向空
  16. while (tail->next != NULL)
  17. {
  18. prev = tail;
  19. tail = tail->next;
  20. }
  21. free(tail);
  22. tail = NULL;
  23. //倒数第二个结点指向空
  24. prev->next = NULL;
  25. }
  26. }

尾删除结点要注意结点空的个数有空,一个结点,多个结点三种情况需要处理

2.6头删结点

  1. //头删数据
  2. void SlistPopFront(SL** pphead)
  3. {
  4. //1.不能为空
  5. assert(*pphead != NULL);
  6. //2.删除头节点
  7. SL* next = (*pphead)->next;
  8. free(*pphead);
  9. *pphead = next;
  10. }

注意删除头节点前要先让记录第二个结点,删除头节点后让其称为新的头节点

2.7查找修改结点

  1. //查找数据,查找的时候也能同时修改这个结点
  2. SL* SlistFind(SL** pphead, SLDataType x)
  3. {
  4. assert(*pphead != NULL);
  5. SL* cur = *pphead;
  6. while (cur)
  7. {
  8. if (cur->val == x)
  9. return cur;
  10. else
  11. cur = cur->next;
  12. }
  13. return NULL;
  14. }

返回这个结点,通过返回值还能修改这个结点的值

2.8在pos位置的前一个位置插入结点

  1. //在pos位置之前插入数据
  2. void SlistInsert(SL** pphead, SL* pos, SLDataType x)
  3. {
  4. //1.创建一个新的结点
  5. SL* newnode = CreatListNode(x);
  6. //2.找到pos位置并在其前面插入数据
  7. if (pos == *pphead)//头插
  8. {
  9. SlistPushFront(pphead, x);
  10. }
  11. else
  12. {
  13. SL* posprev = *pphead;
  14. while (posprev->next != pos)
  15. {
  16. posprev = posprev->next;
  17. }
  18. posprev->next = newnode;
  19. newnode->next = pos;
  20. }
  21. }

当pos位置为头节点直接调用头插结点即可

2.9在pos位置的后面插入结点

  1. //在pos位置之前插入数据
  2. void SlistInsertAfter(SL** phead, SL* pos, SLDataType x)
  3. {
  4. //1.创建一个新的结点
  5. SL* newnode = CreatListNode(x);
  6. //2.插入结点
  7. newnode->next = pos->next;
  8. pos->next = newnode;
  9. }

pos后面插入比较简单

2.10删除pos位置的结点

  1. //删除pos位置的结点
  2. void SlistEarse(SL** pphead, SL* pos)
  3. {
  4. assert(*pphead != NULL);
  5. if (pos == *pphead)//头删
  6. {
  7. SlistPopFront(pphead);
  8. }
  9. else
  10. {
  11. //找到pos位置的前一个结点
  12. SL* posprev = *pphead;
  13. while (posprev->next != pos)
  14. {
  15. posprev = posprev->next;
  16. }
  17. posprev->next = pos->next;
  18. free(pos);
  19. }
  20. }

2.11删除pos位置后面结点

  1. //删除pos位置后面的结点
  2. void SlistEraseAfter(SL* pos)
  3. {
  4. assert(pos != NULL);
  5. SL* next = pos->next;
  6. pos->next = next->next;
  7. free(next);
  8. next = NULL;
  9. }

2.12销毁链表

  1. //销毁整个链表
  2. void SlistDestroy(SL** pphead)
  3. {
  4. SL* cur = *pphead;
  5. while (cur)
  6. {
  7. SL* next = cur->next;
  8. free(cur);
  9. cur == next;
  10. }
  11. free(*pphead);
  12. *pphead = NULL;
  13. }

3.链表的优缺点

1.按需分配空间,要多少给空间就分配多少空间(分配空间合理)

2.插入,删除数据不需要挪动数据,只需修改少数指针

3.不存在空间的浪费

4.链表的缺点

1.不支持随机访问

2.每个数据都需要一个额外的数据来存储

5.顺序表整体代码

链表实现代码-CSDN博客

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

闽ICP备14008679号