• 初阶数据结构学习记录——넷 单链表(2)


    目录

    1、Find

    2、插入pos之后

    3、插入pos之前

    4、删除pos之后

    5、删除pos

    6、销毁

    7、test.c


    接着上一篇写。

    pos位置左右的操作

    这一篇要写的功能如下

    1. SLTNode* SListFind(SLTNode* phead, SLTDataType x);
    2. //单链表在pos位置之后插入x
    3. SLTNode* SListInsertAfter(SLTNode* pos, SLTDataType x);
    4. //在pos之前插入x
    5. SLTNode* SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
    6. //单链表删除pos位置之后的值
    7. SLTNode* SListEraseAfter(SLTNode* pos);
    8. //删除pos位置
    9. SLTNode* SListErase(SLTNode** pphead, SLTNode* pos, SLTDataType x);

    1、Find

    好吧,看到这个就想起了OPPO

    find这个功能是为了要删除pos位置时,能够找到pos位置是哪个节点。直接上代码

    1. SLTNode* SListFind(SLTNode* phead, SLTDataType x)
    2. {
    3. SLTNode* cur = phead;
    4. while (cur)
    5. {
    6. if (cur->data == x)
    7. {
    8. return cur;
    9. }
    10. cur = cur->next;
    11. }
    12. return NULL;
    13. }

    传过来的phead头,都要再建一个局部变量存储它,不能动phead。

    2、插入pos之后

    重点是pos的操作。pos是已经经过find后找到的节点位置,所以删除这个位置之后我们直接使用pos即可。

    1. SLTNode* SListInsertAfter(SLTNode* pos, SLTDataType x)
    2. {
    3. assert(pos);
    4. SLTNode* newnode = BuySLTNode(x);
    5. newnode->next = pos->next;
    6. pos->next = newnode;
    7. }

    根据上面的查找功能,如果遍历所有也没找到,返回一个NULL,那么pos就是NULL。那么也就没必要再插入了,所以断言一下。不过还有一个问题,为什么这里没用二级指针?在之前的尾插函数里,头节点的指针地址传了过来,然后用一个同类型的指针指向它,我们可以用它来连接下一个节点。假设用的是一级指针,那么也就是把头节点的内容传了过来,比如NULL,那么函数里面局部变量tail接收后插入数据,但原本函数外的节点并没有连接上新节点,所以要用二级指针。而这里,pos得到之后,我们要做的是改变结构体里面的成员——结构体指针指向的数据,所以我们只需要一个结构体指针即可改变结构体里面的成员。那么也就不需要二级指针。简而言之,二级指针是为了改变一级指针存储的内容,而用一级指针可以改变另一个一级指针指向哪里,但是要拿到数据就需要二级指针。

    3、插入pos之前

    想插入之前就比较麻烦。遍历到pos位置之后,我们需要一个指针跟在后面,这样也就能指向pos之前的节点了。如果是pos位置为第一个,那就是头插。但是尾插是不存在的,pos即使是最后 i 一个位置,也不是尾插,尾插则是pos已经越界了,所以只考虑头插即可,同样也要断言

    1. SLTNode* SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
    2. {
    3. assert(pos);
    4. if (*pphead == pos)
    5. {
    6. SLTPushFront(pphead, x);
    7. }
    8. else
    9. {
    10. SLTNode* prev = *pphead;
    11. while (prev->next != pos)
    12. {
    13. prev = prev->next;
    14. }
    15. SLTNode* newnode = BuySLTNode(x);
    16. prev->next = newnode;
    17. newnode->next = pos;
    18. }
    19. }

    4、删除pos之后

    关于删除功能,pos从头到尾,特别情况只有尾,如果pos是尾,那么后面就是NULL,那就不用操作。如果free掉pos之后的节点,然后pos->next = pos->next->next,连接再往后一个可行吗?这里会存在野指针。free之后,pos->next指向的空间已经回归内存,那么pos->next->next也就不知道访问到哪里去了,整个程序就会错误。所以我们删除之前先定义一个结构体来存一下pos->next,通过这个定义的结构体来进行操作。

    1. SLTNode* SListEraseAfter(SLTNode* pos)
    2. {
    3. assert(pos);
    4. if (pos->next == NULL)
    5. {
    6. return;
    7. }
    8. else
    9. {
    10. SLTNode* next = pos->next;
    11. pos->next = next->next;
    12. free(next);
    13. }
    14. }

    5、删除pos

    会出现头删。

    1. SLTNode* SListErase(SLTNode** pphead, SLTNode* pos)
    2. {
    3. assert(pos);
    4. assert(*pphead);
    5. if (*pphead == pos)
    6. {
    7. SLTPopFront(pphead);
    8. }
    9. else
    10. {
    11. SLTNode* prev = *pphead;
    12. while (prev->next != pos)
    13. {
    14. prev = prev->next;
    15. }
    16. prev->next = pos->next;
    17. free(pos);
    18. }
    19. }

    6、销毁

    其实和删除有些相同,都需要注意野指针问题

    1. void SLTDestory(SLTNode** pphead)
    2. {
    3. SLTNode* cur = *pphead;
    4. while (cur)
    5. {
    6. SLTNode* next = cur->next;
    7. free(cur);
    8. cur = next;
    9. }
    10. *pphead = NULL;
    11. }

    7、test.c

    1. void TestSList5()
    2. {
    3. SLTNode* plist = NULL;
    4. SLTPushBack(&plist, 1);
    5. SLTPushBack(&plist, 2);
    6. SLTPushBack(&plist, 3);
    7. SLTPushBack(&plist, 4);
    8. SLTPushBack(&plist, 5);
    9. SLTPrint(plist);
    10. SLTNode* p = SLTFind(plist, 3);
    11. SLTInsertAfter(p, 30);
    12. //p = SLTFind(plist, 300);
    13. //SLTInsertAfter(p, 30);
    14. p = SLTFind(plist, 2);
    15. SLTInsert(&plist, p, 20);
    16. /*if (p)
    17. {
    18. SLTInsertAfter(p, 30);
    19. printf("ҵ\n");
    20. }
    21. else
    22. {
    23. printf("Ҳ\n");
    24. }*/
    25. SLTPrint(plist);
    26. }
    27. void TestSList6()
    28. {
    29. SLTNode* plist = NULL;
    30. SLTPushBack(&plist, 1);
    31. SLTPushBack(&plist, 2);
    32. SLTPushBack(&plist, 3);
    33. SLTPushBack(&plist, 4);
    34. SLTPushBack(&plist, 5);
    35. SLTPrint(plist);
    36. SLTNode* p = SLTFind(plist, 3);
    37. SLTEraseAfter(p);
    38. SLTPrint(plist);
    39. p = SLTFind(plist, 3);
    40. SLTErase(&plist, p);
    41. p = NULL;
    42. SLTPrint(plist);
    43. SLTDestroy(&plist);
    44. SLTPrint(plist);
    45. }

    结束。

  • 相关阅读:
    经典机器学习方法(3)—— 多层感知机
    数说故事亮相CPG第八届中国消费品数字科技大会
    MindSpore的tile算子性能问题
    springboot物流配送推荐系统毕业设计源码072257
    一起来学nginx(一)
    蓝桥等考C++组别一级009
    Java 类、属性、方法、this 案例分析
    Java数组详解
    Linux-v10.0 笔记(一)
    【面试:并发篇38:多线程:线程池】ThreadPoolExecutor类的基本概念
  • 原文地址:https://blog.csdn.net/kongqizyd146/article/details/127684618