当前位置:   article > 正文

【C语言】-- 数据结构 -- 单向单链表_c语言单向链表

c语言单向链表

目录

1. 单链表

2.单向单链表的分类

    2.1 带头或者不带头(带头:哨兵位)

    2.2 循环或者非循环  

3. 不带头单向单链表的实现

    3.1 链表的打印

    3.2 动态创建一个节点

    3.3 单链表的尾插

             3.4 单链表的头插

    3.5 单链表的尾删

         3.5.1 只有一个节点时:

         3.5.2 有两个或更多节点时:

    3.6 单链表的头删

         3.6.1 只有一个节点时:

​         3.6.2 有两个或更多节点时:

    3.7 单链表的查找(返回NULL就是没有找到)

    3.8 单链表的插入(之前)

         3.8.1 只有一个节点时: 

         3.8.2 有两个或更多节点时:


1. 单链表

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

其的存在就例如一列火车,我们可以对车厢的链接顺序更改,又必须是一个型号的火车车厢,并且不能说每一节车厢完全属于一起的,连续的。但又进行了链接,属于同一个类型。也就是逻辑上是连续的,但是物理上却不是。从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续。


2.单向单链表的分类

    2.1 带头或者不带头(带头:哨兵位)

    2.2 循环或者非循环  


3. 不带头单向单链表的实现

  1. typedef int SLTDateType; //便于对于单链表的数据存储类型的改变
  2. typedef struct SListNode
  3. {
  4. SLTDateType a; //指向动态开辟的数组
  5. struct SListNode* next; //该节点所指向的下一个节点或NULL(结尾)
  6. }SLTNode;
  7. //动态创建一个节点
  8. SLTNode* SListNewNode(SLTDateType n);
  9. //链表的打印
  10. void SListPrint(SLTNode* pphead);
  11. //单链表的尾插
  12. void SListPushBack(SLTNode** pphead, SLTDateType n);
  13. //单链表的头插
  14. void SListPushFront(SLTNode** pphead, SLTDateType n);
  15. //单链表的尾删
  16. void SListPopBack(SLTNode** pphead);
  17. //单链表的头删
  18. void SListPopFrond(SLTNode** pphead);
  19. //单链表的查找(返回NULL就是没有找到)
  20. SLTNode* SListFind(SLTNode* phead, SLTDateType n);
  21. //单链表的插入(之前)
  22. void SListInsertForward(SLTNode** pphead, SLTNode* pos, SLTDateType n);

    3.1 链表的打印

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

    3.2 动态创建一个节点

        是极为重要的一个操作,只要是关于插入的功能都需要运用,所以此处将其进行封装,便于使用。

  1. //动态创建一个节点
  2. SLTNode* SListNewNode(SLTDateType n)
  3. {
  4. SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
  5. assert(newnode);
  6. newnode->a = n;
  7. newnode->next = NULL;
  8. return newnode;
  9. }


    3.3 单链表的尾插

        **pphead == &head

        主要针对于无节点的单链表链表,对于此类单链表如若按常理执行,会访问结构指针成员(struct SListNode* next),会导致野指针的问题。正确的操作就是直接对等于NULL的指针head直接进行更改,使得我们所插入的数据变为head所指向的。

        这个操作,改变的不是head所可指向的数据,而是其本身。而更改其本身,是无法通过传参的形式更改,只会是一个临时拷贝,无法进行直接的更改,于是就需要运用二级指针。传入&head,以**pphead接收。 

  1. //单链表的尾插
  2. void SListPushBack(SLTNode** pphead, SLTDateType n)
  3. {
  4. assert(pphead);
  5. SLTNode* newnode = SListNewNode(n);
  6. if (NULL == *pphead)
  7. {
  8. *pphead = newnode;
  9. }
  10. else
  11. {
  12. SLTNode* cur = *pphead;
  13. while (NULL != cur->next)
  14. {
  15. cur = cur->next;
  16. }
  17. cur->next = newnode;
  18. }
  19. }

 

(注意(动态图制作有问题):newnode节点的next应该是NULL)


    3.4 单链表的头插

  1. //单链表的头插
  2. void SListPushFront(SLTNode** pphead, SLTDateType n)
  3. {
  4. assert(pphead);
  5. SLTNode* newnode = SListNewNode(n);
  6. newnode->next = *pphead;
  7. *pphead = newnode;
  8. }


    3.5 单链表的尾删

        此功能,是很容易就能实现的,直接free最后一个节点,然后再将前一个节点中的next改为NULL即可。但,需要注意phead是不能为空的,不然怎么删除?

        我们对于含有元素的链表,是需要最后一个节点与倒数第二个节点。

        3.5.1 只有一个节点时:

         3.5.2 有两个或更多节点时:

  1. //单链表的尾删
  2. void SListPopBack(SLTNode** pphead)
  3. {
  4. assert(pphead);
  5. assert(*pphead);
  6. SLTNode* cur = *pphead;
  7. if (NULL == cur->next) //只有一个节点时
  8. {
  9. free(*pphead);
  10. *pphead = NULL;
  11. }
  12. else //有两个或更多节点时
  13. {
  14. while (NULL != cur->next->next)
  15. {
  16. cur = cur->next;
  17. }
  18. free(cur->next);
  19. cur->next = NULL;
  20. }
  21. }

    3.6 单链表的头删

        需要注意phead是不能为空的,不然根本没有节点供给删除。

  1. //单链表的头删
  2. void SListPopFrond(SLTNode** pphead)
  3. {
  4. assert(pphead);
  5. assert(*pphead);
  6. if (NULL == (*pphead)->next) //只有一个节点时
  7. {
  8. free(*pphead);
  9. *pphead = NULL;
  10. }
  11. else //有两个或更多节点时
  12. {
  13. SLTNode* cur = (*pphead)->next;
  14. free(*pphead);
  15. *pphead = cur;
  16. }
  17. }

         3.6.1 只有一个节点时:

          3.6.2 有两个或更多节点时:

 


    3.7 单链表的查找(返回NULL就是没有找到)

  1. //单链表的查找(返回NULL就是没有找到)
  2. SLTNode* SListFind(SLTNode* phead, SLTDateType n)
  3. {
  4. assert(phead);
  5. SLTNode* cur = phead;
  6. while (n != cur->a)
  7. {
  8. cur = cur->next;
  9. if (NULL == cur)
  10. {
  11. return NULL;
  12. }
  13. }
  14. return cur;
  15. }

    3.8 单链表的插入(之前)

        与头插差不多,只不过同样的是得到插入的前面一个节点的地址,,正因为所传入的pos就是所插入的后一个节点的地址,于是通过cur->next确定是不是下一个就是其。 

  1. //单链表的插入(之前)
  2. void SListInsertForward(SLTNode** pphead, SLTNode* pos, SLTDateType n)
  3. {
  4. assert(pphead);
  5. assert(*pphead);
  6. assert(pos);
  7. SLTNode* newnode = SListNewNode(n);
  8. SLTNode* cur = *pphead;
  9. if (cur == pos)
  10. {
  11. newnode->next = *pphead;
  12. *pphead = newnode;
  13. }
  14. else
  15. {
  16. while (pos != cur->next)
  17. {
  18. cur = cur->next;
  19. }
  20. newnode->next = cur->next;
  21. cur->next = newnode;
  22. }
  23. }

          3.8.1 只有一个节点时: 

          3.8.2 有两个或更多节点时:

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

闽ICP备14008679号