• 链表(1)


    目录

    单链表

    主函数test.c

    test1

    test2

    test3

    test4

    头文件&函数声明SList.h

    函数实现SList.c

    打印SLPrint

    创建节点CreateNode

    尾插SLPushBack

    头插SLPushFront

    头删SLPopBck

    尾删SLPopFront

    易错点


    本篇开始链表学习。今天主要是单链表&OJ题目。

    单链表

    前面的博文我们讲了顺序表。顺序表的优势就是【物理空间的连续】,就只需要一个指针指向开始位置,用数组下标去访问即可。但是这也是它的劣势。当插入和删除数据需要挪动数据。

    无论是【顺序表】还是【链表】里的数据,任何类型都可。所以用typedef。

    在开始阶段,线性表可能是物理空间上连续【顺序表】,可能是逻辑顺序上连续【链表】。链表的优势就是,删除和插入数据不需要挪动,空间可以一块一块的释放,不会影响其他节点。链表每个节点都是独立的。

    【链表】的种类很多,今天先介绍【无头单项不循环链表】----【单链表】。

    主函数test.c

    #include"SList.h"
    1. int main()
    2. {
    3. SLNode* phead = NULL;//结构体指针变量存放结构体的地址 头节点
    4. test1(&phead);//测试尾插
    5. test2(&phead);//测试头插
    6. test3(&phead);//测试尾删
    7. test4(&phead);//测试头删
    8. return 0;
    9. }

    test1

    1. void test1(SLNode** pphead)//测试尾插
    2. {
    3. SLPushBack(pphead, 10);
    4. SLPushBack(pphead, 20);
    5. SLPushBack(pphead, 30);
    6. SLPushBack(pphead, 40);
    7. SLPrint(*pphead);
    8. }

    test2

    1. void test2(SLNode** pphead)//测试头插
    2. {
    3. SLPushFront(pphead, 77);
    4. SLPushFront(pphead, 66);
    5. SLPushFront(pphead, 55);
    6. SLPushFront(pphead, 33);
    7. SLPrint(*pphead);
    8. }

    test3

    1. void test3(SLNode** pphead)//测试头删
    2. {
    3. SLPopFront(pphead);
    4. SLPopFront(pphead);
    5. SLPopFront(pphead);
    6. SLPrint(*pphead);
    7. }

    test4

    1. void test4(SLNode** pphead)//测试尾删
    2. {
    3. SLPopBack(pphead);
    4. SLPopBack(pphead);
    5. SLPrint(*pphead);
    6. }

    头文件&函数声明SList.h

    1. #pragma once
    2. #include
    3. #include
    4. #include
    • 创建单链表
    1. //创建单链表
    2. typedef int SLNDataType;//单链表节点数据类型
    3. typedef struct SListNode//创建节点
    4. {
    5. SLNDataType val;
    6. struct SListNode* next;
    7. }SLNode;

    ?为什么 SListNode 还未创建好,就可以在结构体内部使用这个 SListNode 了

    因为next是一个结构体指针变量,主体是指针变量,无影响。但是如果是 struct SListNode next;不可以,结构体嵌套结构体是不可以的。


    •  打印数据
    1. //打印数据
    2. void SLPrint(SLNode* phead);
    • 尾插
    1. //尾插
    2. void SLPushBack(SLNode** pphead, SLNDataType x);
    • 头插
    1. //头插
    2. void SLPushFront(SLNode** pphead, SLNDataType x);
    • 头删
    1. //头删
    2. void SLPopFront(SLNode** pphead);
    • 尾删 
    1. //尾删
    2. void SLPopBack(SLNode** pphead);

    函数实现SList.c

    #include"SList.h"

    打印SLPrint

    • 不要让phead移动
    1. void SLPrint(SLNode* phead)
    2. {
    3. assert(phead);
    4. SLNode* tail = phead;
    5. printf("phead->");
    6. while (tail->next != NULL)
    7. {
    8. printf("%d->", tail->val);
    9. tail = tail->next;
    10. }
    11. printf("NULL");
    12. printf("\n");
    13. }

    创建节点CreateNode

    1. //创建链表的节点---结构体
    2. SLNode* CreateNode(SLNDataType x)
    3. {
    4. SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
    5. if (newnode == NULL)
    6. {
    7. perror("malloc");
    8. exit(-1);//直接终止程序
    9. //return;
    10. }
    11. newnode->val = x;
    12. newnode->next = NULL;
    13. return newnode;
    14. }

    尾插SLPushBack

    • 二级指针的使用,不然就会链接不起来,出了函数栈帧局部变量就销毁了。
    • 改变外部的变量,一定有一个解引用的操作
    • 多情况的考虑
    1. //尾插
    2. void SLPushBack(SLNode** pphead, SLNDataType x)
    3. {
    4. //assert(*pphead);
    5. SLNode* newnode = CreateNode(x);
    6. //无节点
    7. if (*pphead == NULL)
    8. {
    9. *pphead = newnode;
    10. }
    11. //多个节点
    12. else
    13. {
    14. SLNode* tail = *pphead;
    15. while (tail->next != NULL)
    16. {
    17. tail = tail->next;
    18. }
    19. tail->next = newnode;
    20. }
    21. }

    头插SLPushFront

    • 代码书写的先后顺序
    • 二级指针 
    1. //头插
    2. void SLPushFront(SLNode** pphead, SLNDataType x)
    3. {
    4. //assert(*pphead);
    5. SLNode* newnode = CreateNode(x);
    6. newnode->next = *pphead;
    7. *pphead = newnode;
    8. }

    头删SLPopBck

    • 代码书写的先后顺序
    • 二级指针 
    1. //头删
    2. void SLPopFront(SLNode** pphead)
    3. {
    4. assert(*pphead);
    5. SLNode* tail = *pphead;
    6. *pphead = (*pphead)->next;
    7. free(tail);
    8. tail = NULL;
    9. }

     

    尾删SLPopFront

    • 多种情况的考虑 
    1. //尾删
    2. void SLPopBack(SLNode** pphead)
    3. {
    4. assert(*pphead);
    5. //一个节点
    6. if ((*pphead)->next == NULL)
    7. {
    8. free(*pphead);
    9. *pphead = NULL;
    10. }
    11. else
    12. {
    13. SLNode* tail = *pphead;
    14. SLNode* prve = tail;
    15. while (tail->next != NULL)
    16. {
    17. prve = tail;
    18. tail = tail->next;
    19. }
    20. prve->next = NULL;
    21. free(tail);
    22. tail = NULL;
    23. }
    24. }

     


    易错点

    • 断言❌
    • 无节点/一个节点/多节点的考虑❌
    • 传值调用/传址调用(二级指针使用)❌
    • 记住:要修改头节点(头节点是结构体指针变量的指向必须用二级指针❌
    • 空间的释放(不是释放指针变量,释放的是指针指向的空间)❌
    • *pphead&*pphead->next辨析❌
    • 野指针的诞生❌

    代码---------→【唐棣棣 (TSQXG) - Gitee.com

    联系---------→【邮箱:2784139418@qq.com】

  • 相关阅读:
    UI自动化测试之Jenkins配置
    最新Java JDK 21:全面解析与新特性探讨
    Android中C++如何读写json文件
    vite+vue+cesium搭建工程:有社区插件方便
    如何使用 Midjourney换脸,将一个人面部复制并粘贴到任意人身上
    GBRank:一种基于回归的排序方法
    自定义输入密码控件
    HTML+CSS简单的网页制作期末作业——浙江旅游景点介绍网页制作
    9. SQL中Insert into/Update/Delete的用法
    数据库学习之数据类型
  • 原文地址:https://blog.csdn.net/m0_74841364/article/details/134196213