当前位置:   article > 正文

数据结构-链表-单链表(3)

数据结构-链表-单链表(3)

目录

1. 顺序表的缺陷

2. 单链表

2.1 单链表的基本结构与接口函数

2.2 重要接口

创建新节点的函数:

2.2.1 尾插

2.2.2 头插

2.2.3 尾删

2.2.4 头删

2.2.5 查找

2.2.6 插入

2.2.7 删除

2.2.8 从pos后面插入

2.2.9 从pos后面删除

3. 链表的缺陷与优势:

4. 链表与顺序表比较

写在最后:


1. 顺序表的缺陷

为什么会有链表?

我们都有顺序表来存储数据了,

因为顺序表是有缺陷的:

1. 中间头部插入删除数据,需要挪动数据,效率低下。

2. 空间不够需要扩容,扩容会有一定的消耗,也可能会造成空间的浪费。

这时候,我们就要用到链表。

2. 单链表

链表是一种物理存储结构上非连续、非顺序的存储结构,

数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

如下图: 

2.1 单链表的基本结构与接口函数

基本结构:

  1. #pragma once
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <assert.h>
  5. typedef int SLDataType;
  6. typedef struct SListNode
  7. {
  8. SLDataType data;
  9. struct SListNode* next;
  10. }SLNode;
  11. //打印链表
  12. void SLPrint(SLNode* phead);
  13. //尾插
  14. void SLPushBack(SLNode** pphead, SLDataType x);
  15. //头插
  16. void SLPushFront(SLNode** pphead, SLDataType x);
  17. //尾删
  18. void SLPopBack(SLNode** ppheadx);
  19. //头删
  20. void SLPopFront(SLNode** pphead);
  21. //查找
  22. SLNode* SLFind(SLNode* phead, SLDataType x);
  23. //插入
  24. void SLInsert(SLNode** phead, SLNode* pos, SLDataType x);
  25. //删除
  26. void SLErase(SLNode** phead, SLNode* pos);
  27. //从pos后面插入
  28. void SLInsertAfter(SLNode* pos, SLDataType x);
  29. //从pos后面删除
  30. void SLEraseAfter(SLNode* pos);

2.2 重要接口

创建新节点的函数:

  1. //建立一个新节点(重复操作写成函数复用)
  2. SLNode* BuyNewNode(SLDataType x)
  3. {
  4. //malloc一个链表节点大小的空间
  5. SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
  6. //检查
  7. if (newnode == NULL)
  8. {
  9. perror("malloc::fail");
  10. return;
  11. }
  12. //赋值
  13. newnode->data = x;
  14. newnode->next = NULL;
  15. return newnode;
  16. }

2.2.1 尾插

  1. //尾插
  2. void SLPushBack(SLNode** pphead, SLDataType x)
  3. {
  4. //检查二级指针pphead地址是否为空
  5. //方便检查是否传空指针了
  6. assert(pphead);
  7. //创建节点
  8. SLNode* newnode = BuyNewNode(x);
  9. //如果链表为空
  10. if (*pphead == NULL)
  11. {
  12. *pphead = newnode;
  13. }
  14. else//如果链表不为空
  15. {
  16. //找尾
  17. SLNode* tail = *pphead;
  18. while (tail->next != NULL)
  19. {
  20. tail = tail->next;
  21. }
  22. //尾插
  23. tail->next = newnode;
  24. }
  25. }

2.2.2 头插

  1. //头插
  2. void SLPushFront(SLNode** pphead, SLDataType x)
  3. {
  4. //检查二级指针pphead地址是否为空
  5. //方便检查是否传空指针了
  6. assert(pphead);
  7. //创建节点
  8. SLNode* newnode = BuyNewNode(x);
  9. //头插
  10. newnode->next = *pphead;
  11. *pphead = newnode;
  12. }

2.2.3 尾删

  1. //尾删
  2. void SLPopBack(SLNode** pphead)
  3. {
  4. //检查二级指针pphead地址是否为空
  5. //方便检查是否传空指针了
  6. assert(pphead);
  7. //检查链表是否为空
  8. assert(*pphead);
  9. //头删的情况(链表只有一个数据)
  10. if ((*pphead)->next == NULL)
  11. {
  12. free(*pphead);
  13. *pphead = NULL;
  14. }
  15. else
  16. {
  17. //找尾
  18. SLNode* tail = *pphead;
  19. while (tail->next->next != NULL)
  20. {
  21. tail = tail->next;
  22. }
  23. //尾删
  24. free(tail->next);
  25. tail->next = NULL;
  26. }
  27. }

2.2.4 头删

  1. //头删
  2. void SLPopFront(SLNode** pphead)
  3. {
  4. //检查二级指针pphead地址是否为空
  5. //方便检查是否传空指针了
  6. assert(pphead);
  7. //检查链表是否为空
  8. assert(*pphead);
  9. //头删
  10. SLNode* cur = (*pphead)->next;
  11. free(*pphead);
  12. *pphead = NULL;
  13. *pphead = cur;
  14. }

2.2.5 查找

  1. //查找
  2. SLNode* SLFind(SLNode* phead, SLDataType x)
  3. {
  4. //创建指针遍历链表
  5. SLNode* cur = phead;
  6. //查找
  7. while (cur)
  8. {
  9. if (cur->data == x)
  10. {
  11. return cur;
  12. }
  13. cur = cur->next;
  14. }
  15. return NULL;
  16. }

2.2.6 插入

  1. //插入
  2. void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x)
  3. {
  4. //检查查找的地址是否为空
  5. assert(pos);
  6. //pos的位置
  7. if (pos == *pphead)
  8. {
  9. SLPushFront(pphead, x);
  10. }
  11. else//在链表中间
  12. {
  13. SLNode* prev = *pphead;
  14. //查找pos对应位置
  15. while (prev->next != pos)
  16. {
  17. prev = prev->next;
  18. }
  19. //插入
  20. SLNode* newnode = BuyNewNode(x);
  21. prev->next = newnode;
  22. newnode->next = pos;
  23. }
  24. }

2.2.7 删除

  1. //删除
  2. void SLErase(SLNode** pphead, SLNode* pos)
  3. {
  4. //检查二级指针pphead地址是否为空
  5. //方便检查是否传空指针了
  6. assert(pphead);
  7. //检查查找的地址是否为空
  8. assert(pos);
  9. //检查链表是否为空(这里其实不断言也可以)
  10. assert(*pphead);
  11. //头删的情况
  12. if (*pphead == pos)
  13. {
  14. SLPopFront(pphead);
  15. }
  16. else
  17. {
  18. //查找
  19. SLNode* prev = *pphead;
  20. while (prev->next != pos)
  21. {
  22. prev = prev->next;
  23. }
  24. //删除
  25. prev->next = pos->next;
  26. free(pos);
  27. pos = NULL;
  28. }
  29. }

2.2.8 从pos后面插入

  1. //从pos后面插入
  2. void SLInsertAfter(SLNode* pos, SLDataType x)
  3. {
  4. //检查查找的地址是否为空
  5. assert(pos);
  6. //创建节点
  7. SLNode* newnode = BuyNewNode(x);
  8. //再pos后面插入
  9. newnode->next = pos->next;
  10. pos->next = newnode;
  11. }

2.2.9 从pos后面删除

  1. //从pos后面删除
  2. void SLEraseAfter(SLNode* pos)
  3. {
  4. //检查查找的地址和要删除的地址是否为空
  5. assert(pos);
  6. assert(pos->next);
  7. //在pos后面删除,prev记住要删除的节点,然后free
  8. SLNode* prev = pos->next;
  9. pos->next = prev->next;
  10. free(pos->next);
  11. prev = NULL;
  12. }

这就是单链表的基本框架和接口了,

如果感兴趣,你也可以使用接口函数玩一玩。

3. 链表的缺陷与优势:

但是链表是有缺陷的,

我们可以看到,

1. 链表想要访问一个节点,就得遍历链表,

2. 链表的尾删也需要遍历链表,

3. 而链表的优势是头删很方便是O(1)。

4. 链表与顺序表比较

我们就能跟顺序表比较一下,

1. 顺序表头插需要挪动数据是O(N),

2. 但是顺序表能随机访问。

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果喜欢本文的话,欢迎点赞和评论,写下你的见解。

如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。

之后我还会输出更多高质量内容,欢迎收看。

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

闽ICP备14008679号