• 【单链表增删查改接口的实现】


    大家好呀,今天向大家分享的是一些单链表增删查改接口具体的实现,如果哪儿有什么不对的地方希望各位佬能够帮忙指正一下。

     

     

    目录

     1 尾插和头插

    2 尾删和头删

    3 查找(查增和查删)

    4 释放


    1 尾插和头插

    首先我们自己先定义一个结构体类型:

    1. typedef struct SListNode
    2. {
    3. SLTDateType data;
    4. struct SListNode* next;
    5. }SListNode;

    尾插的具体实现:

    1. SListNode* SLCreatNode(x)
    2. {
    3. SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));
    4. if (tmp != NULL)
    5. {
    6. tmp->data = x;
    7. tmp->next = NULL;
    8. return tmp;
    9. }
    10. }
    11. void SLPushBack(SListNode** pNode, SLTDateType x)
    12. {
    13. SListNode* newnode=SLCreatNode(x);
    14. if (*pNode == NULL)
    15. {
    16. *pNode = newnode;
    17. }
    18. else
    19. {
    20. SListNode* tail = *pNode;
    21. while (tail->next)
    22. {
    23. tail = tail->next;
    24. }
    25. tail->next = newnode;
    26. }
    27. }

    因为头插和尾插都需要malloc出空间,所以为了代码的简练我们就分装了一个函数SLCreatNode来完成空间的动态开辟。尾插中值得注意的小细节是当*pNode为NULL的时候要单独处理,不然执行tail->next就会出错。

    头插的具体实现:

    1. void SLPushFront(SListNode** pNode, SLTDateType x)
    2. {
    3. SListNode* newnode = SLCreatNode(x);
    4. newnode->next = *pNode;
    5. *pNode = newnode;
    6. }

    头插比较简单,这里就不多讲了。为了能够看见具体效果,我们可以实现一个打印接口。

    具体代码:

    1. void SListPrint(SListNode* pNode)
    2. {
    3. SListNode* cur = pNode;
    4. while (cur)
    5. {
    6. printf("%d->", cur->data);
    7. cur = cur->next;
    8. }
    9. printf("NULL");
    10. }

     

    现在我们可以来看结果了:

    2e004e6ab38c441aaa5564715c42a7ff.png

     通过效果图可以看出尾插和头插都是正确的。


    2 尾删和头删

    尾删的具体代码:

    1. void SLPopBack(SListNode** pNode)
    2. {
    3. //methon1:
    4. /*if (*pNode == NULL)
    5. {
    6. return;
    7. }*/
    8. //method2:
    9. assert(*pNode != NULL);//(结点为空)
    10. if (((*pNode)->next) == NULL) //(一个结点)
    11. {
    12. free(*pNode);
    13. *pNode = NULL;
    14. }
    15. //method1:
    16. /*else
    17. {
    18. SListNode* prev = NULL;
    19. SListNode* tail = *pNode;
    20. while (tail->next)
    21. {
    22. prev = tail;
    23. tail = tail->next;
    24. }
    25. free(tail);
    26. tail = NULL;
    27. prev->next = NULL;
    28. }*/
    29. //method2:
    30. else//(多个结点)
    31. {
    32. SListNode* tail = *pNode;
    33. while (tail->next->next)
    34. {
    35. tail = tail->next;
    36. }
    37. free(tail->next);
    38. tail->next = NULL;
    39. }
    40. }

    尾删的细节处理比较多,细节有时候没有处理好就会导致程序挂掉。我们首先来看当1 结点为空的情况:处理这种情况一般有两种方法

    温柔版:if (*pNode == NULL)
                        {
                             return;
                       }

    暴力版:assert(*pNode != NULL);

     具体哪种方法可以凭借自己喜好选择。

    2 结点为多个:

    第一种方法是不创建额外的临时变量:判断tail->next->next是否为空,为空就停止,然后free掉tail->next,再将tail->next置为NULL,第二种方法是创建临时变量prev,具体代码可以参照上面。其实无论是哪一种方法,都是要记住要free掉的空间的上一个地址,通过这个地址将其改为NULL。

    3 结点为一个:

    当结点为一个的时候无论采取上面的哪一种方法都会造成对NULL的访问,这样肯定是行不通的,所以结点为一个的时候就要单独处理。

    头删的具体代码:

    1. void SLPopFront(SListNode** pNode)
    2. {
    3. if (*pNode==NULL)
    4. {
    5. return;
    6. }
    7. //一个结点与多个结点的情况可以同时处理
    8. else
    9. {
    10. SListNode* head = (*pNode)->next;
    11. free(*pNode);
    12. *pNode = head;
    13. }
    14. }

    整体思路与尾删差不多,有些不同的是当结点为一个或者多个的时候可以同时处理。

    来看一下结果:

    f43d360bd2a54a389a36f559070e048d.png

     如果再尾删一个的话:

    55595d6229984f379cc2231efa52853f.png

     由于尾删我们采取的是暴力的方式,编译器会直接给我们报出错误在哪一行。


    3 查找(查增和查删)

    首先来看一看查找的具体代码:

    1. SListNode* SListFind(SListNode* pNode, SLTDateType x)
    2. {
    3. SListNode* cur = pNode;
    4. while (cur)
    5. {
    6. if (cur->data == x)
    7. {
    8. return cur;
    9. }
    10. else
    11. {
    12. cur = cur->next;
    13. }
    14. }
    15. return NULL;
    16. }

    为什么查找的返回值要用SListNode*呢?

    这样的好处是为了方便与查增与查删一起使用,并且还很方便修改值:

    例如:

    8984ea4fe9d64060a90c28b6b7f8aa21.png

     这样我们就将3改为了我们想要的300,那如果我们想要在3的前面或者后面插入一个数呢?

    如果是想在3的前面插入一个数,这个时候我们就必须一个一个从头遍历,找到要插入的pos位的上一位地址,还要分类讨论第一个位置是否为要查找的位置,代码量也比较大,这也是单链表的缺陷。

    在pos位前插入代码具体实现:

    1. void SListInsert(SListNode** pNode, SListNode* pos, SLTDateType x)
    2. {
    3. SListNode* newnode = SLCreatNode(x);
    4. if (*pNode == pos)
    5. {
    6. newnode->next = *pNode;
    7. *pNode = newnode;
    8. }
    9. else
    10. {
    11. SListNode* posProve = *pNode;
    12. while (posProve->next != pos)
    13. {
    14. posProve = posProve->next;
    15. }
    16. posProve->next = newnode;
    17. newnode->next = pos;
    18. }
    19. }

    其中需要注意的是当第一个元素就是查找元素的时候,这个就要分开判断,这种情况就相当于头插。

    来看看这种查找后插入的效果:

    1af3e4d1c3b24ba08c769eb2188fbf61.png

     那如果是直接后插呢?

    直接上代码:

    1. void SLInsertAfter(SListNode* pos, SLTDateType x)
    2. {
    3. if (pos != NULL)
    4. {
    5. SListNode* newnode = SLCreatNode(x);
    6. newnode->next = pos->next;
    7. pos->next = newnode;
    8. }
    9. }

    结果展示:

    2e2526b4a2b24178992085e976cfb5b3.png

     这样在后面插入的话代码量会少很多。

    删除pos位的具体代码:

    1. void SListErase(SListNode** pNode, SListNode* pos)
    2. {
    3. assert(pNode);
    4. if (*pNode == pos)//头删
    5. {
    6. *pNode = pos->next;
    7. free(pos);
    8. }
    9. else
    10. {
    11. SListNode* posPrev = *pNode;
    12. while (posPrev->next != pos)
    13. {
    14. posPrev = posPrev->next;
    15. }
    16. posPrev->next = pos->next;
    17. free(pos);
    18. pos = NULL;
    19. }
    20. }

    要想删除pos位,就必须知道前一位的地址,所以只能从头开始遍历,而当第一位就是pos位的时候,就相当于头删,所以就要分开讨论,具体代码可以参考上面。

    结果展示:

    c32ab5c0490846a19efa9fb4c057ed26.png

    其实单链表进行删除pos位是比较麻烦的,要从头开始遍历,但是如果只是想删除pos位后面的一位就很容易,而且可以不用传入plist的地址。

    具体代码实现:

    1. void SLiEraseAfter(SListNode* pos)
    2. {
    3. assert(pos->next);
    4. SListNode* posNext = pos->next;
    5. pos->next = posNext->next;
    6. free(posNext);
    7. posNext = NULL;
    8. }

    效果图:

    60758677103f45958d0e34fa6133e5a6.png

     


    4 释放

    既然空间是动态开辟的,那么当不用该空间时就要还给操作系统,那么直接free掉*pNode不就好了吗?如果直接free掉*pNode会导致后面空间的地址丢失,那不就造成内存泄漏了吗,所以这样肯定是行不通的,正确的做法应该是用一个临时指针来保存当前地址。

    具体代码:

    1. void SListDestroy(SListNode** pNode)
    2. {
    3. SListNode* cur = *pNode;
    4. while (cur)
    5. {
    6. SListNode* Next = cur->next;
    7. free(cur);
    8. cur = NULL;
    9. cur = Next;
    10. }
    11. *pNode = NULL;
    12. }

    好了,今天的分享就到这里了,如果该文对你有帮助的话能不能3连支持一下博主呢?

    1fe60d7f205040aeb8b5169839f324d8.png

     

     

  • 相关阅读:
    苹果电脑误删文件怎么找回?苹果电脑删了文件能恢复吗?苹果电脑文件删除怎么恢复
    freeswitch隐藏fs标识
    【数据库系统概论】数据模型
    【LeetCode】恢复二叉搜索树 [M](Morris遍历)
    【简单讲解下epoll】
    Dubbo启动报错
    【小程序项目开发--京东商城】uni-app之自定义搜索组件(上)
    从0到1手把手教你ASP.NET Core Web API项目配置接口文档Swagger(一)
    React批处理原理及性能优化实践
    C++动态内存管理+模板
  • 原文地址:https://blog.csdn.net/m0_68872612/article/details/126060325