• 【数据结构】 单链表:无头+单向+非循环链表增删查改实现


     链表的学习,要多画图,分析其中的逻辑关系,便于理解

    下面是对单链表所有操作的代码以及详细注释,便于复习

    目录

    整体声明

    创建节点

    单链表尾插

    单链表的尾删

    单链表打印

    单链表的头插

    单链表头删

    单链表查找

    任意位置插入

    任意位置的删除

    链表中所有的节点计数

    单链表的销毁



            我们对【无头+单向+非循环】链表实现增删查改的功能分别进行代码实现,并对步骤尽可能进行详细分析。

    整体声明

    // 单链表中的常规操作
    // 单链表尾插
    // 注意:并不是所有的方法都要在头文件中声明
    //      一般只要将其他用户需要调用的方法在头文件中声明

    1. // 单链表节点的定义
    2. typedef struct SListNode
    3. {
    4. DataType data; // 节点中的值域
    5. struct SListNode* next; // 下一个节点的地址
    6. }SListNode;
    7. ///
    8. // 单链表中的常规操作
    9. // 单链表尾插
    10. // 注意:并不是所有的方法都要在头文件中声明
    11. // 一般只要将其他用户需要调用的方法在头文件中声明
    12. void SListPushBack(SListNode** pplist, DataType x);
    13. // 单链表的尾删
    14. void SListPopBack(SListNode** pplist);
    15. // 单链表的头插
    16. void SListPushFront(SListNode** pplist, DataType x);
    17. // 单链表头删
    18. void SListPopFront(SListNode** pplist);
    19. // 单链表查找
    20. SListNode* SListFind(SListNode* plist, DataType x);
    21. // 单链表在pos位置之后插入x
    22. // 分析思考为什么不在pos位置之前插入?
    23. void SListInsertAfter(SListNode* pos, DataType x);
    24. // 单链表删除pos位置之后的值
    25. // 分析思考为什么不删除pos位置?
    26. void SListEraseAfter(SListNode* pos);
    27. void SListDestroy(SListNode** plist);
    28. // 测试链表
    29. void TestSList();

    创建节点

    链表内部自己实现的时候要使用的方式
    用户可以不用知道,该方法不需要在头文件中声明

    1. SListNode* BuySListNode(DataType x)
    2. {
    3. SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
    4. if (NULL == newNode)
    5. {
    6. printf("创建节点失败!!!\n");
    7. exit(0); // 暴力方式处理
    8. }
    9. newNode->data = x;
    10. newNode->next = NULL;
    11. return newNode;
    12. }

    单链表尾插

    pplist内部现在保存的时候实参plist的地址


    *pplist ===> 实参plist

    1. void SListPushBack(SListNode** pplist, DataType x)
    2. {
    3. assert(pplist); // pplist是空:链表不存在
    4. // 空链表---需要让pplist指向刚刚插入的新节点
    5. if (NULL == *pplist)
    6. {
    7. *pplist = BuySListNode(x);
    8. }
    9. else
    10. {
    11. // pplist的链表不为空
    12. // 1. 找到原链表中的最后一个节点
    13. SListNode* cur = *pplist;
    14. while (cur->next)
    15. {
    16. // 获取cur的下一个节点
    17. // cur++; // 注意下一个节点是通过next指针域获取
    18. cur = cur->next;
    19. }
    20. // 2. 插入新节点
    21. cur->next = BuySListNode(x);
    22. }
    23. }

    单链表的尾删

    1. void SListPopBack(SListNode** pplist)
    2. {
    3. assert(pplist); // 保证:pplist指向实参plist
    4. // 1. 空链表
    5. if (NULL == *pplist)
    6. return;
    7. else if (NULL == (*pplist)->next)
    8. {
    9. // 2. 链表中只有一个节点
    10. free(*pplist);
    11. *pplist = NULL;
    12. }
    13. else
    14. {
    15. // 3. 链表中有多个节点(至少是2个)
    16. // a. 找到最后一个节点并保存其前一个节点
    17. SListNode* cur = *pplist;
    18. SListNode* prev = NULL; // 保存cur的前一个节点
    19. while (cur->next)
    20. {
    21. prev = cur;
    22. cur = cur->next;
    23. }
    24. // cur是最后一个节点 prev刚好是cur的前一个
    25. free(cur);
    26. prev->next = NULL;
    27. }
    28. }

    单链表打印

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

    单链表的头插

    /将x插入到第一个节点之前----插入成功后x就是链表中第一个节点

    1. //时间复杂度0(1)
    2. void SListPushFront(SListNode** pplist, DataType x)
    3. {
    4. //将x插入到第一个节点之前----插入成功后x就是链表中第一个节点
    5. assert(pplist);
    6. //申请一个新的节点
    7. SListNode* newNode = BuySListNode(x);
    8. if (*pplist == NULL) //链表为空
    9. {
    10. *pplist = newNode;
    11. }
    12. else
    13. {
    14. newNode->next = *pplist;
    15. *pplist = newNode;
    16. }
    17. }

    单链表头删

    1. void SListPopFront(SListNode** pplist)
    2. {
    3. assert(pplist);
    4. //空链表直接返回
    5. if (NULL == *pplist)
    6. {
    7. return;
    8. }
    9. //只有一个节点
    10. else if (NULL == (*pplist)->next)
    11. {
    12. free(*pplist);
    13. *pplist = NULL;
    14. }
    15. //多个节点, delNode为要删除的节点
    16. else
    17. {
    18. SListNode* delNode = *pplist;//把第一个节点定义为要删除的节点
    19. *pplist = delNode->next; //让*pplist指向下一个节点
    20. free(delNode);
    21. }
    22. }

    单链表查找

    //遍历整个链表,将x与结点中的值域进行比较,相等,直接改节点地址
        //如果不相等,继续往后便利 cur = cur->next,直到都没有,返回null

    1. SListNode* SListFind(SListNode* plist, DataType x)
    2. {
    3. //遍历整个链表,将x与结点中的值域进行比较,相等,直接改节点地址
    4. //如果不相等,继续往后便利 cur = cur->next,直到都没有,返回null
    5. SListNode* cur = plist;
    6. while (cur != NULL)
    7. {
    8. if (x == cur->data)
    9. {
    10. return cur;
    11. }
    12. cur = cur->next;
    13. }
    14. return NULL;
    15. }

    任意位置插入

    // 单链表在pos位置之后插入x
    // 分析思考为什么不在pos位置之前插入?
    //不带头节点的单链表,新节点无法插入到pos之前,而且我们不知道链表的第一个节点,无法遍历
    //无法知道pos的前一个是谁,所以只能插入到pos之后

    1. void SListInsertAfter(SListNode* pos, DataType x) //任意位置插入
    2. {
    3. SListNode* newNode = NULL;
    4. if (NULL == pos)
    5. return;
    6. newNode = BuySListNode(x);
    7. newNode->next = pos->next;
    8. pos->next = newNode;
    9. }

    任意位置的删除

    // 单链表删除pos位置之后的值

    1. // 单链表删除pos位置之后的值
    2. // 分析思考为什么不删除pos位置?
    3. void SListEraseAfter(SListNode* pos) //任意位置的删除,实际删除pos之后的节点
    4. {
    5. SListNode* delNode = NULL;
    6. //pos不为空且不是最后一个节点
    7. if (pos == NULL || pos->next == NULL)
    8. return;
    9. delNode = pos->next;
    10. if (NULL == delNode)
    11. return;
    12. pos->data = delNode->data;
    13. pos->next = delNode->next;
    14. free(delNode);
    15. }

    链表中所有的节点计数

    1. int SListSize(SListNode* plist) //当前链表中所有的节点计数
    2. {
    3. SListNode* cur = plist;
    4. int count = 0;
    5. while (cur)
    6. {
    7. count++;
    8. cur = cur->next;
    9. }
    10. return count;
    11. }

    单链表的销毁

    1. void SListDestroy(SListNode** pplist)//单链表的销毁
    2. {
    3. assert(pplist);
    4. SListNode* cur = *pplist;
    5. while (cur)
    6. {
    7. *pplist = cur->next;
    8. free(cur);
    9. cur = *pplist;
    10. }
    11. *pplist = NULL;
    12. }

  • 相关阅读:
    Linux的NFS配置
    微信小程序实现左滑删除
    如何精准地找工作
    判断DataFrame中是否存在具有相同内容的行将具有相同内容的行进行标记和处理
    vmlogin指纹浏览器中设置本地API进行常规自动化操作
    自建网盘平台搭建(源码+教程)
    二、模型驱动测试设计
    OpenAI官方吴达恩《ChatGPT Prompt Engineering 提示词工程师》(5)转换 / Transforming翻译
    Docker
    随机森林random forest和Stepwise Clustered Ensemble (SCE)
  • 原文地址:https://blog.csdn.net/weixin_59215611/article/details/126459062