常用数据结构和算法

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

这里面有10个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie树;10个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法。。

这边有个数据结构的可视化工具:https://pythontutor.com/render.html#mode=display


一、复杂度

1、时间复杂度(最好情况时间复杂度、最坏情况时间复杂度、平均时间复杂度)

2、空间复杂度

二、数据结构知识

2.1 数据结构的逻辑结构

数据结构中的逻辑结构(附上c++中的常见数据结构的逻辑结构)_数据结构的逻辑结构有哪些-CSDN博客

数据结构的逻辑结构包括四种逻辑结构

1、集合结构

2、线性结构:一一对应

3、树形结构

4、图形结构

三、数据结构分析

3.1、链表

3.1.1 静态链表

3.2.2动态链表

3.2.2.1、回顾代码

看了一个抖音,里面讲了静态链表和动态链表。之前看的静态链表讲的蛮好的。觉得还挺简单的。今天又看到了讲动态链表的。当时就有个疑问。new出来的stu_new的next指针不需要值NULL吗?一直带着这个疑问。还评论了一下,想看看别人是怎么说的。然后回工位还在想这件事情。然后就觉得写一下吧,感觉不是很难,然后就写下了下面的代码。

下面的代码基本实现了视频里的效果。后面还要跟视频的作下对比。

#include <stdio.h>
#include <stdlib.h>
#include "string.h"

//动态指针
//①枚举类型不会写(成员不会写)
//②结构体指针写法不会写
//scanf循环


//性别
typedef enum Gender
{
    male,
    female,
    middle
}Gend;

//结构体
typedef struct stu
{
    char name[10];
    int age;
    Gend gender;
    int score;
    struct stu* next;
}Stu;

Stu stuDynamic;

int stuAdd(Stu* stu)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu *stuTemp = &stuDynamic;

    while(stuTemp->next)
    {
        stuTemp = stuTemp->next;
    }

    stuTemp->next = stu;

    return 1;
}

void printStu(Stu* stu)
{
    Stu* temp = stu->next;
    printf("打印学生信息\n");
    while(temp)
    {
        printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);
        temp = temp->next;
    }
}

int main()
{
    char name[10] = {'\0'};
    int age = -1;
    int score;
    Gend gender;
    printf("输入姓名, 年龄, 性别, 分数\n");
//    gets("%s", name);
//    scanf("%d", &age);
//    scanf("%d", &score);
//    scanf("%d", &gender);

    while(age != 0)
    {
        scanf("%s %d %d %d", name, &age, &gender, &score);
        Stu *stu_new = (Stu*)malloc(sizeof(Stu));
        if(NULL == stu_new)
        {
            printf("malloc fail");
            continue;
        }

        memcpy(stu_new->name, name, 10);
        stu_new->age = age;
        stu_new->score = score;
        stu_new->gender = gender;
        stu_new->next = NULL;


        stuAdd(stu_new);

        printStu(&stuDynamic);
    }

    return 0;
}

但是其中还是有一个问题。就是我一开始写的代码是没有将stu_new的next指针置空的。那个代码就异常了。体现在没有让我继续输入数据,然后也打印了,打印后就退出了。 然后调试的时候发现程序在print里面会异常退出。

现在来看就是因为stu_new没有置为NULL。所以我之前的考虑是对的。那么视频里的程序是怎么写的呢,需要在会看一下视频代码。下面就把之前写的代码贴出来。

#include <stdio.h>
#include <stdlib.h>
#include "string.h"

//动态指针
//①枚举类型不会写(成员不会写)
//②结构体指针写法不会写
//scanf循环


//性别
typedef enum Gender
{
    male,
    female,
    middle
}Gend;

//结构体
typedef struct stu
{
    char name[10];
    int age;
    Gend gender;
    int score;
    struct stu* next;
}Stu;

Stu stuDynamic;

int stuAdd(Stu* stu)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu *stuTemp = &stuDynamic;

    while(stuTemp->next)
    {
        stuTemp = stuTemp->next;
    }

    stuTemp->next = stu;

    return 1;
}

void printStu(Stu* stu)
{
    Stu* temp = stu;
    printf("打印学生信息\n");
    while(temp)
    {
        printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);
        temp = temp->next;
    }
}

int main()
{
    char name[10] = {'\0'};
    int age = -1;
    int score;
    Gend gender;
    printf("输入姓名, 年龄, 性别, 分数\n");
//    gets("%s", name);
//    scanf("%d", &age);
//    scanf("%d", &score);
//    scanf("%d", &gender);

    while(age != 0)
    {
        scanf("%s %d %d %d", name, &age, &gender, &score);
        Stu *stu_new = (Stu*)malloc(sizeof(Stu));
        if(NULL == stu_new)
        {
            printf("malloc fail");
            continue;
        }

        memcpy(stu_new->name, name, 10);
        stu_new->age = age;
        stu_new->score = score;
        stu_new->gender = gender;

        stuAdd(stu_new);

        printStu(&stuDynamic);
    }

    return 0;
}

所以当stu_new的next指针没有置为NULL的时候,程序会越界访问。导致程序崩溃结束。看起来的效果就像是程序执行完了一样。

随后还有个问题,就是全局变量stuDynamic的next指针时NULL吗?

自己写了代码验证

#include <stdio.h>
#include <stdlib.h>
#include "string.h"

Stu stuDynamic;

int main()
{
    if(NULL == stuDynamic.next)
    {
        printf("stuDynamic->next:%p\n", stuDynamic.next);
        printf("stuDynamic->next is NULL\n");
    }
}

//stuDynamic->next:00000000
//stuDynamic->next is NULL

查了一个帖子:全局变量和局部变量初始化问题_全局变量需要初始化吗-CSDN博客

里面这么表述:这里需要分清一个事实,是变量系统都会默认给初始化,只不过全局变量默认初始化为0,而局部变量被初始化为随机数,这个随机数用不了,后面会验证

3.2.2.2、阶段总结

看了视频代码,有以下几点需要说明下

①全局变量没有写成g_xxx

②视频代码重复输入是因为用了while(1)

③视频的打印也是从g_stu开始打印,所以有全是0的输出,name是空

后面可以优化代码。

下面是优化代码,下面的代码实现了修改和查询

#include <stdio.h>
#include <stdlib.h>
#include "string.h"
#include "stdbool.h"

//动态指针
//①枚举类型不会写(成员不会写)
//②结构体指针写法不会写
//③scanf循环
//④scanf用法
//⑤switch-case写法
//⑥strcpy == 0,没有判断是否==0,导致直接打印0
//⑦解决scanf读单个字符会把换行符\n读取,且字符变量中只存了\n
//⑧将strcmp用错成strcpy,导致出现修改时所有数据都被改了


//性别
typedef enum Gender
{
    male,
    female,
    middle
}Gend;

//结构体
typedef struct stu
{
    char name[10];
    int age;
    Gend gender;
    int score;
    struct stu* next;
}Stu;

Stu g_stuDaynamic;

//增加
int stu_add(Stu* stu)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu *stuTemp = &g_stuDaynamic;

    while(stuTemp->next)
    {
        stuTemp = stuTemp->next;
    }

    stuTemp->next = stu;

    return 1;
}

//查询
int stu_find(Stu* stu, char* name)
{
    Stu *temp = stu;
    while(NULL == temp)
    {
        return -1;
    }

    while(temp)
    {
        if( strcmp(temp->name, name) == 0)
        {
            printf("find the record");
            printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);

            return 1;
        }
        temp = temp->next;
    }

    printf("find the record Empty!");
}

//修改
int stu_modify(const Stu* stu, Stu* stu_modify)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu* temp = stu;
    while(temp)
    {
        if(strcmp(temp->name, stu_modify->name) == 0)
        {
            (*temp).age = stu_modify->age;
            (*temp).gender = stu_modify->gender;
            (*temp).score = stu_modify->score;

            return 1;
        }
        temp = temp->next;
    }
}

void printStu(Stu* stu)
{
    Stu* temp = stu->next;
    printf("打印学生信息\n");
    while(temp)
    {
        printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);
        temp = temp->next;
    }
}

int action_add()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    printf("输入姓名, 年龄, 性别, 分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);
    Stu* stu_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_new)
    {
        printf("malloc fail");
        return -1;
    }

    memcpy(stu_new->name, name, 10);
    stu_new->age = age;
    stu_new->score = score;
    stu_new->gender = gender;
    stu_new->next = NULL;

    stu_add(stu_new);

    printStu(&g_stuDaynamic);
}

int action_find(char* name)
{
    stu_find(&g_stuDaynamic, name);
    return 0;
}

int action_modify()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    Stu *stu_modify_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_modify_new)
    {
        printf("malloc fail");
        return -1;
    }

    printf("输入修改的姓名, 年龄, 性别, 分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);

    memcpy(stu_modify_new->name, name, 10);
    stu_modify_new->age = age;
    stu_modify_new->score = score;
    stu_modify_new->gender = gender;

    stu_modify(&g_stuDaynamic, stu_modify_new);

    free(stu_modify_new);

    printStu(&g_stuDaynamic);

    return 1;
}

int main()
{
    bool m_flag = true;
    if(NULL == g_stuDaynamic.next)
    {
        printf("g_stuDaynamic->next:%p\n", g_stuDaynamic.next);
        printf("g_stuDaynamic->next is NULL\n");
    }

    while(m_flag)
    {
        printf("选择操作(A:增加、D:删除、C:更改、F:查找、其他:退出):\n");
        char action[2];
        scanf("%s", action);
        switch(action[0])
        {
            case 'A':
            {
                action_add();
                break;
            }

            case 'F':
            {
                printf("输入查询的姓名:\n");
                char name[100];
                scanf("%s", name);
                action_find(name);
                break;
            }

            case 'C':
            {
                action_modify();
                break;
            }

            default:
            {
                m_flag = false;
                break;
            }
        }
    }

    return 0;
}



这个代码还在编译时出现一个警告:

warning:initialization discards ‘const’ qualifier from pointer target type

这个帖子就是解释这个告警:warning:initialization discards ‘const’ qualifier from pointer target type 解决方法_initialization discards 'const' qualifier from poi-CSDN博客

那么就存在一个问题,就是我不想函数更改g_stuDaynamic变量。所以用const限定为只读变量。但是在函数中还要遍历链表,temp又不能增加const修饰。但是赋值时就出现这个告警。下次看看教科书别人是怎么处理的。

还有一个删除的功能没有实现,这个会涉及到链表头、尾的问题,先阶段性提交一次。

下面是增加删除的代码

#include <stdio.h>
#include <stdlib.h>
#include "string.h"
#include "stdbool.h"
#include "ctype.h"

//动态指针
//①枚举类型不会写(成员不会写)
//②结构体指针写法不会写
//③scanf循环
//④scanf用法
//⑤switch-case写法
//⑥strcpy == 0,没有判断是否==0,导致直接打印0
//⑦解决scanf读单个字符会把换行符\n读取,且字符变量中只存了\n


//性别
typedef enum Gender
{
    male,
    female,
    middle
}Gend;

//结构体
typedef struct stu
{
    char name[10];
    int age;
    Gend gender;
    int score;
    struct stu* next;
}Stu;

Stu g_stuDaynamic;

int stu_add(Stu* stu)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu *stuTemp = &g_stuDaynamic;

    while(stuTemp->next)
    {
        stuTemp = stuTemp->next;
    }

    stuTemp->next = stu;

    return 1;
}

int stu_find(Stu* stu, char* name)
{
    Stu *temp = stu;
    while(NULL == temp)
    {
        return -1;
    }

    while(temp)
    {
        if( strcmp(temp->name, name) == 0)
        {
            printf("find the record");
            printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);

            return 1;
        }
        temp = temp->next;
    }

    printf("find the record Empty!");
}

int stu_modify(const Stu* stu, Stu* stu_modify)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu* temp = stu;
    while(temp)
    {
        if(strcmp(temp->name, stu_modify->name) == 0)
        {
            (*temp).age = stu_modify->age;
            (*temp).gender = stu_modify->gender;
            (*temp).score = stu_modify->score;

            return 1;
        }
        temp = temp->next;
    }
}

//删除
int stu_delete(Stu* stu, char* name)
{
    if (NULL == stu)
    {
        return -1;
    }
    
    Stu* temp = stu;
    while (temp)
    {
        if(NULL != temp->next && strcmp(temp->next->name, name) == 0)
        {
            //目标节点在重点
            Stu* traget = temp->next;
            Stu* tragetNext = traget->next;
            temp->next = tragetNext;
            free(traget);

            printf("已找到记录并删除\n");
            return 1;
        }
        temp = temp->next;
    }
    
    printf("未找到可删除记录\n");
    return 0;
}

void printStu(Stu* stu)
{
    Stu* temp = stu->next;
    printf("打印学生信息\n");
    while(temp)
    {
        printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);
        temp = temp->next;
    }
}

int action_add()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    printf("输入姓名, 年龄, 性别, 分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);
    Stu* stu_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_new)
    {
        printf("malloc fail");
        return -1;
    }

    memcpy(stu_new->name, name, 10);
    stu_new->age = age;
    stu_new->score = score;
    stu_new->gender = gender;
    stu_new->next = NULL;

    stu_add(stu_new);

    printStu(&g_stuDaynamic);
}

int action_find(char* name)
{
    stu_find(&g_stuDaynamic, name);
    return 0;
}

int action_modify()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    Stu *stu_modify_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_modify_new)
    {
        printf("malloc fail");
        return -1;
    }

    printf("输入修改的姓名, 年龄, 性别, 分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);

    memcpy(stu_modify_new->name, name, 10);
    stu_modify_new->age = age;
    stu_modify_new->score = score;
    stu_modify_new->gender = gender;

    stu_modify(&g_stuDaynamic, stu_modify_new);

    free(stu_modify_new);

    printStu(&g_stuDaynamic);

    return 1;
}

int action_delete()
{
    printf("输入需要删除的记录:\n");
    char* name[100];
    scanf("%s", name);
    stu_delete(&g_stuDaynamic, name);
    printStu(&g_stuDaynamic);
    return 0;
}

bool isTargetAlpha(char c)
{
    if(toupper(c) == 'A' || toupper(c) == 'D' || toupper(c) == 'C' || toupper(c) == 'F' || toupper(c) == 'Q')
    {
        return true;
    }

    return false;
}

int main()
{
    bool m_flag = true;
    if(NULL == g_stuDaynamic.next)
    {
        printf("g_stuDaynamic->next:%p\n", g_stuDaynamic.next);
        printf("g_stuDaynamic->next is NULL\n");
    }

    while(m_flag)
    {
        printf("选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):\n");
        char action[2];
        scanf("%s", action);
        if(!isTargetAlpha(action[0]))
        {
            continue;
        }
        switch(toupper(action[0]))
        {
            case 'A':
            {
                action_add();
                break;
            }

            case 'F':
            {
                printf("输入查询的姓名:\n");
                char name[100];
                scanf("%s", name);
                action_find(name);
                break;
            }

            case 'C':
            {
                action_modify();
                break;
            }

            case 'D':
            {
                action_delete();
                break;
            }

            case 'Q':
            {
                m_flag = false;
                printf("退出\n");
                break;
            }

            default:
            {
                printf("操作有误,请重新输入\n");
                break;
            }
        }        
    }

    return 0;
}



感觉比想象的简单,没有之前那么复杂啊。

反正自验证实现了功能,目前还遗留的就是输错信息,程序直接退出了。需要优化下。

另外印象中删除好像还挺复杂呢?

2024年1月11日20:01:11

下面就是所谓的复杂的。现在写的是插入。好像删除也不复杂,之前印象中复杂的只有插入。不然链表也太难了吧。同时参考C和指针的书籍。其实这个代码一直你有一个问题,就是我在函数里把g_stuDaynamic写死了,不能处理其他的链表,没有通用性。这个也是看C和指针的书才发现的啊!!



#include "test.h"

//动态指针
//①枚举类型不会写(成员不会写)
//②结构体指针写法不会写
//③scanf循环
//④scanf用法
//⑤switch-case写法
//⑥strcpy == 0,没有判断是否==0,导致直接打印0
//⑦解决scanf读单个字符会把换行符\n读取,且字符变量中只存了\n


//性别
typedef enum Gender
{
    male,
    female,
    middle
}Gend;

//结构体
typedef struct stu
{
    char name[10];
    int age;
    Gend gender;
    int score;
    struct stu* next;
}Stu;

Stu g_stuDaynamic;

int stu_add(Stu* stu)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu *stuTemp = &g_stuDaynamic;

    while(stuTemp->next)
    {
        stuTemp = stuTemp->next;
    }

    stuTemp->next = stu;

    return 1;
}

//插入数据有序排列
void stu_insert(Stu* new)
{
    Stu* previous;
    Stu* current;
    Stu* next;

    previous = &g_stuDaynamic;
    current = g_stuDaynamic.next;

    while(current->next != NULL && current->score <= new->score )
    {
        previous = current;
        current = current->next;
    }

    if(current->next == NULL)
    {
        current->next = new;
    }
    else
    {
        previous->next = new;
        new->next = current;
    }
}

int stu_find(Stu* stu, char* name)
{
    Stu *temp = stu;
    while(NULL == temp)
    {
        return -1;
    }

    while(temp)
    {
        if( strcmp(temp->name, name) == 0)
        {
            printf("find the record");
            printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);

            return 1;
        }
        temp = temp->next;
    }

    printf("find the record Empty!");
}

int stu_modify(const Stu* stu, Stu* stu_modify)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu* temp = stu;
    while(temp)
    {
        if(strcmp(temp->name, stu_modify->name) == 0)
        {
            (*temp).age = stu_modify->age;
            (*temp).gender = stu_modify->gender;
            (*temp).score = stu_modify->score;

            return 1;
        }
        temp = temp->next;
    }
}

//删除
int stu_delete(Stu* stu, char* name)
{
    if (NULL == stu)
    {
        return -1;
    }

    Stu* temp = stu;
    while (temp)
    {
        if(NULL != temp->next && strcmp(temp->next->name, name) == 0)
        {
            //目标节点在重点
            Stu* traget = temp->next;
            Stu* tragetNext = traget->next;
            temp->next = tragetNext;
            free(traget);

            printf("已找到记录并删除\n");
            return 1;
        }
        temp = temp->next;
    }

    printf("未找到可删除记录\n");
    return 0;
}

void printStu(Stu* stu)
{
    Stu* temp = stu->next;
    printf("打印学生信息\n");
    while(temp)
    {
        printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);
        temp = temp->next;
    }
}

int action_add()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    printf("输入姓名, 年龄, 性别, 分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);
    Stu* stu_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_new)
    {
        printf("malloc fail");
        return -1;
    }

    memcpy(stu_new->name, name, 10);
    stu_new->age = age;
    stu_new->score = score;
    stu_new->gender = gender;
    stu_new->next = NULL;

    stu_add(stu_new);

    printStu(&g_stuDaynamic);
}

//插入数据进行有序排列
int action_insert()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    printf("输入姓名、年龄、性别、分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);
    Stu* stu_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_new)
    {
        printf("malloc fail");
        return -1;
    }

    memcpy(stu_new->name, name, 10);
    stu_new->age = age;
    stu_new->score = score;
    stu_new->gender = gender;
    stu_new->next = NULL;

    stu_insert(stu_new);

    printStu(&g_stuDaynamic);
}

int action_find(char* name)
{
    stu_find(&g_stuDaynamic, name);
    return 0;
}

int action_modify()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    Stu *stu_modify_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_modify_new)
    {
        printf("malloc fail");
        return -1;
    }

    printf("输入修改的姓名, 年龄, 性别, 分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);

    memcpy(stu_modify_new->name, name, 10);
    stu_modify_new->age = age;
    stu_modify_new->score = score;
    stu_modify_new->gender = gender;

    stu_modify(&g_stuDaynamic, stu_modify_new);

    free(stu_modify_new);

    printStu(&g_stuDaynamic);

    return 1;
}

int action_delete()
{
    printf("输入需要删除的记录:\n");
    char* name[100];
    scanf("%s", name);
    stu_delete(&g_stuDaynamic, name);
    printStu(&g_stuDaynamic);
    return 0;
}

bool isTargetAlpha(char c)
{
    if(toupper(c) == 'A' || toupper(c) == 'D' || toupper(c) == 'C' || toupper(c) == 'F' || toupper(c) == 'Q' || toupper(c) == 'I')
    {
        return true;
    }

    return false;
}

//int i = 0;
//
//void add() const
//{
//    i++;
//}

int list_func()
{
//    add();

    bool m_flag = true;
    if(NULL == g_stuDaynamic.next)
    {
        printf("g_stuDaynamic->next:%p\n", g_stuDaynamic.next);
        printf("g_stuDaynamic->next is NULL\n");
    }

    while(m_flag)
    {
        printf("选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):\n");
//        printf("选择操作(A:增加、D:删除、C、更改:\n");
//        printf("hello");
        char action[2];
        scanf("%s", action);
        if(!isTargetAlpha(action[0]))
        {
            continue;
        }
        switch(toupper(action[0]))
        {
            case 'A':
            {
                action_add();
                break;
            }

            case 'F':
            {
                printf("输入查询的姓名:\n");
                char name[100];
                scanf("%s", name);
                action_find(name);
                break;
            }

            case 'C':
            {
                action_modify();
                break;
            }

            case 'D':
            {
                action_delete();
                break;
            }

            case 'Q':
            {
                m_flag = false;
                printf("退出\n");
                break;
            }

            //插入数据有序排列
            case 'I':
            {
                action_insert();
                break;
            }

            default:
            {
                printf("操作有误,请重新输入\n");
                break;
            }
        }


    }

    return 0;
}


//g_stuDaynamic->next:00000000
//g_stuDaynamic->next is NULL
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//A
//输入姓名, 年龄, 性别, 分数
//zhangs 7 0 77
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//A
//输入姓名, 年龄, 性别, 分数
//lilu 8 1 88
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//姓名:lilu, 年龄:8, 性别:1, 成绩:88
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//A
//输入姓名, 年龄, 性别, 分数
//wangmei 9 1 99
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//姓名:lilu, 年龄:8, 性别:1, 成绩:88
//姓名:wangmei, 年龄:9, 性别:1, 成绩:99
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//I
//输入姓名、年龄、性别、分数
//xux 6 0 90
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//姓名:lilu, 年龄:8, 性别:1, 成绩:88
//姓名:wangmei, 年龄:9, 性别:1, 成绩:99
//姓名:xux, 年龄:6, 性别:0, 成绩:90
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//I
//输入姓名、年龄、性别、分数
//xiaom 6 1 80
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//姓名:xiaom, 年龄:6, 性别:1, 成绩:80
//姓名:lilu, 年龄:8, 性别:1, 成绩:88
//姓名:wangmei, 年龄:9, 性别:1, 成绩:99
//姓名:xux, 年龄:6, 性别:0, 成绩:90
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//I
//输入姓名、年龄、性别、分数
//xiaoh 9 0 83
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//姓名:xiaom, 年龄:6, 性别:1, 成绩:80
//姓名:xiaoh, 年龄:9, 性别:0, 成绩:83
//姓名:lilu, 年龄:8, 性别:1, 成绩:88
//姓名:wangmei, 年龄:9, 性别:1, 成绩:99
//姓名:xux, 年龄:6, 性别:0, 成绩:90
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):


上面的代码插入尾部的时候会异常,下面的可以正常插入尾部

#include "test.h"

//动态指针
//①枚举类型不会写(成员不会写)
//②结构体指针写法不会写
//③scanf循环
//④scanf用法
//⑤switch-case写法
//⑥strcpy == 0,没有判断是否==0,导致直接打印0
//⑦解决scanf读单个字符会把换行符\n读取,且字符变量中只存了\n


//性别
typedef enum Gender
{
    male,
    female,
    middle
}Gend;

//结构体
typedef struct stu
{
    char name[10];
    int age;
    Gend gender;
    int score;
    struct stu* next;
}Stu;

Stu g_stuDaynamic;

int stu_add(Stu* stu)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu *stuTemp = &g_stuDaynamic;

    while(stuTemp->next)
    {
        stuTemp = stuTemp->next;
    }

    stuTemp->next = stu;

    return 1;
}

//插入数据有序排列
//存在最后一个节点判断异常
void stu_insert1(Stu* new)
{
    Stu* previous;
    Stu* current;
    Stu* next;

    previous = &g_stuDaynamic;
    current = g_stuDaynamic.next;

    while(current->next != NULL && current->score <= new->score )
    {
        previous = current;
        current = current->next;
    }

    if(current->next == NULL)
    {
        current->next = new;
    }
    else
    {
        previous->next = new;
        new->next = current;
    }
}

//处理有些复杂
void stu_insert2(Stu* new)
{
    Stu* previous;
    Stu* current;
    Stu* next;

    previous = &g_stuDaynamic;
    current = g_stuDaynamic.next;

    while(current->score <= new->score && current->next != NULL)
    {
        previous = current;
        current = current->next;
    }

    //这边处理起来有些吃力,感觉需要识别是因为到结尾了还是当前节点大于插入数据
    if(current->score > new->score)
    {
        previous->next = new;
        new->next = current;
    }
    else
    {
        current->next = new;
    }
}

//C和指针照着写
void stu_insert3(Stu* new)
{
    Stu* previous;
    Stu* current;
    Stu* next;

    previous = NULL;
    current = &g_stuDaynamic;

    while(current->score <= new->score && current != NULL)
    {
        previous = current;
        current = current->next;
    }

    //这边处理起来有些吃力,感觉需要识别是因为到结尾了还是当前节点大于插入数据
    if(current->score > new->score)
    {
        previous->next = new;
        new->next = current;
    }
    else
    {
        current->next = new;
    }
}

//C和指针照着写,程序死机
void stu_insert4(Stu* new)
{
    Stu* previous;
    Stu* current;
    Stu* next;

    previous = NULL;
    current = &g_stuDaynamic;

    while( current->score <= new->score && current != NULL)         //这边内存泄漏,访问其他内存,没有先判断current是否为空
    {
        previous = current;
        current = current->next;
    }

    //这边处理起来有些吃力,感觉需要识别是因为到结尾了还是当前节点大于插入数据
    if(current->score > new->score)
    {
        previous->next = new;
        new->next = current;
    }
    else
    {
        previous->next = new;
    }
}

//C和指针照着写,程序死机
void stu_insert5(Stu* new)
{
    Stu* previous;
    Stu* current;
    Stu* next;

    previous = NULL;
    current = &g_stuDaynamic;

    while( current->score <= new->score && current != NULL)         //这边内存泄漏,访问其他内存,没有先判断current是否为空
    {
        previous = current;
        current = current->next;
    }

    //这边处理起来有些吃力,感觉需要识别是因为到结尾了还是当前节点大于插入数据

    if(current->score > new->score)         //这边内存泄露,current=NULL时,内存泄漏
    {
        previous->next = new;
        new->next = current;
    }
    else
    {
        previous->next = new;
    }
}

void stu_insert(Stu* new)
{
    Stu* previous;
    Stu* current;
    Stu* next;

    previous = NULL;
    current = &g_stuDaynamic;

    while(current != NULL && current->score <= new->score)
    {
        previous = current;
        current = current->next;
    }

    //这边处理起来有些吃力,感觉需要识别是因为到结尾了还是当前节点大于插入数据
    if(current == NULL)
    {
        previous->next = new;
    }
    else
    {
        previous->next = new;
        new->next = current;
    }
}

int stu_find(Stu* stu, char* name)
{
    Stu *temp = stu;
    while(NULL == temp)
    {
        return -1;
    }

    while(temp)
    {
        if( strcmp(temp->name, name) == 0)
        {
            printf("find the record");
            printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);

            return 1;
        }
        temp = temp->next;
    }

    printf("find the record Empty!");
}

int stu_modify(const Stu* stu, Stu* stu_modify)
{
    if(NULL == stu)
    {
        return -1;
    }

    Stu* temp = stu;
    while(temp)
    {
        if(strcmp(temp->name, stu_modify->name) == 0)
        {
            (*temp).age = stu_modify->age;
            (*temp).gender = stu_modify->gender;
            (*temp).score = stu_modify->score;

            return 1;
        }
        temp = temp->next;
    }
}

//删除
int stu_delete(Stu* stu, char* name)
{
    if (NULL == stu)
    {
        return -1;
    }

    Stu* temp = stu;
    while (temp)
    {
        if(NULL != temp->next && strcmp(temp->next->name, name) == 0)
        {
            //目标节点在重点
            Stu* traget = temp->next;
            Stu* tragetNext = traget->next;
            temp->next = tragetNext;
            free(traget);

            printf("已找到记录并删除\n");
            return 1;
        }
        temp = temp->next;
    }

    printf("未找到可删除记录\n");
    return 0;
}

void printStu(Stu* stu)
{
    Stu* temp = stu->next;
    printf("打印学生信息\n");
    while(temp)
    {
        printf("姓名:%s, 年龄:%d, 性别:%d, 成绩:%d\n", temp->name, temp->age, temp->gender, temp->score);
        temp = temp->next;
    }
}

int action_add()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    printf("输入姓名, 年龄, 性别, 分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);
    Stu* stu_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_new)
    {
        printf("malloc fail");
        return -1;
    }

    memcpy(stu_new->name, name, 10);
    stu_new->age = age;
    stu_new->score = score;
    stu_new->gender = gender;
    stu_new->next = NULL;

    stu_add(stu_new);

    printStu(&g_stuDaynamic);
}

//插入数据进行有序排列
int action_insert()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    printf("输入姓名、年龄、性别、分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);
    Stu* stu_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_new)
    {
        printf("malloc fail");
        return -1;
    }

    memcpy(stu_new->name, name, 10);
    stu_new->age = age;
    stu_new->score = score;
    stu_new->gender = gender;
    stu_new->next = NULL;

    stu_insert(stu_new);

    printStu(&g_stuDaynamic);
}

int action_find(char* name)
{
    stu_find(&g_stuDaynamic, name);
    return 0;
}

int action_modify()
{
    char name[10] = {'\0'};
    int age;
    int score;
    Gend gender;

    Stu *stu_modify_new = (Stu*)malloc(sizeof(Stu));
    if(NULL == stu_modify_new)
    {
        printf("malloc fail");
        return -1;
    }

    printf("输入修改的姓名, 年龄, 性别, 分数\n");
    scanf("%s %d %d %d", name, &age, &gender, &score);

    memcpy(stu_modify_new->name, name, 10);
    stu_modify_new->age = age;
    stu_modify_new->score = score;
    stu_modify_new->gender = gender;

    stu_modify(&g_stuDaynamic, stu_modify_new);

    free(stu_modify_new);

    printStu(&g_stuDaynamic);

    return 1;
}

int action_delete()
{
    printf("输入需要删除的记录:\n");
    char* name[100];
    scanf("%s", name);
    stu_delete(&g_stuDaynamic, name);
    printStu(&g_stuDaynamic);
    return 0;
}

bool isTargetAlpha(char c)
{
    if(toupper(c) == 'A' || toupper(c) == 'D' || toupper(c) == 'C' || toupper(c) == 'F' || toupper(c) == 'Q' || toupper(c) == 'I')
    {
        return true;
    }

    return false;
}

int list_func()
{
    bool m_flag = true;
    if(NULL == g_stuDaynamic.next)
    {
        printf("g_stuDaynamic->next:%p\n", g_stuDaynamic.next);
        printf("g_stuDaynamic->next is NULL\n");
    }

    while(m_flag)
    {
        printf("选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):\n");
        char action[2];
        scanf("%s", action);
        if(!isTargetAlpha(action[0]))
        {
            continue;
        }
        switch(toupper(action[0]))
        {
            case 'A':
            {
                action_add();
                break;
            }

            case 'F':
            {
                printf("输入查询的姓名:\n");
                char name[100];
                scanf("%s", name);
                action_find(name);
                break;
            }

            case 'C':
            {
                action_modify();
                break;
            }

            case 'D':
            {
                action_delete();
                break;
            }

            case 'Q':
            {
                m_flag = false;
                printf("退出\n");
                break;
            }

            //插入数据有序排列
            case 'I':
            {
                action_insert();
                break;
            }

            default:
            {
                printf("操作有误,请重新输入\n");
                break;
            }
        }


    }

    return 0;
}


//g_stuDaynamic->next:00000000
//g_stuDaynamic->next is NULL
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//A
//输入姓名, 年龄, 性别, 分数
//zhangs 7 0 77
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//A
//输入姓名, 年龄, 性别, 分数
//lilu 8 1 88
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//姓名:lilu, 年龄:8, 性别:1, 成绩:88
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//A
//输入姓名, 年龄, 性别, 分数
//wangmei 9 0 99
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//姓名:lilu, 年龄:8, 性别:1, 成绩:88
//姓名:wangmei, 年龄:9, 性别:0, 成绩:99
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):
//I
//输入姓名、年龄、性别、分数
//lili 5 1 101
//打印学生信息
//姓名:zhangs, 年龄:7, 性别:0, 成绩:77
//姓名:lilu, 年龄:8, 性别:1, 成绩:88
//姓名:wangmei, 年龄:9, 性别:0, 成绩:99
//姓名:lili, 年龄:5, 性别:1, 成绩:101
//选择操作(A:增加、D:删除、C:更改、F:查找、Q:退出):

几经波折啊!

3.2.2.2.2 阶段2

现在可以实现数据的增加、插入、删除、查找、修改了。但是离C和指针还有一些 差别或者差距。

差距在于我这个目前使用的是一个全局链表变量。所以所有的程序只能针对于这个链表运行。因为函数中写死了链表变量。

所以这是我又想到一个点。就是怎么叫解耦或者什么叫解耦。那就是函数中不能出现全局变量或者写死某个值或变量。

我的这段代码就是没有解耦,比如stu_insert函数中就出现这样的代码

previous = &g_stuDaynamic;
current = g_stuDaynamic.next;

高度耦合。

所以后面的代码修改方向就是需要解耦。

3.2.2.3 解耦代码

2024年1月16日 11:16:16

下面是解耦的代码,

搞的头炸啊!

//lianbiao.c文件

#include "lianbiao.h"

void func()
{
    printf("hello\n");
}

bool isTargetCharacter(char* choose)
{
    if(choose[0] == 'A' || choose[0] == 'I' || choose[0] == 'D' || choose[0] == 'M' || choose[0] == 'F')
    {
        return true;
    }

    return false;
}

//这边printf也是有问题的,我也是把跟指针当做value=0在用,从第二个节点开始打印。同理,我的add函数也是等于是从value=0结构后面加的
void print_lianbiao(lianbiao* stu)
{
    lianbiao* root = stu;
    lianbiao* current = root->next;
    while(current != NULL)
    {
        printf("姓名:%s,年龄:%d,性别:%d,成绩:%d\n", current->name, current->age, current->gender, current->score);
        current = current->next;
    }
}

int stu_add1(lianbiao* stu, lianbiao* new_stu)
{
    while(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = stu;
    lianbiao** current = &previous->next;

    while(*current != NULL)
    {
        *current = (*current)->next;
    }

    *current = new_stu;
    return 1;
}

int stu_add2(lianbiao* stu, lianbiao* new_stu)
{
    while(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = stu;
    lianbiao* current = previous->next;

    while(current != NULL)
    {
        current = current->next;
    }

    lianbiao** p = &current;
    *p = new_stu;
    return 1;
}

//吊毛,槽,这样写竟然就可以了,妈的,自己捣鼓一晚上都没弄好。看了书就好了
int stu_add(lianbiao* stu, lianbiao* new_stu)
{
    while(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = stu;           //stu是二级指针,解引用一次,里面的成员都可以被成功赋值
    lianbiao* current = previous->next;     //现在复盘,大概是previous->next可以被赋值。但是现在的操作是previous->next赋值给previous,再去更改previous的值就更改不了next的值

    while(current != NULL)
    {
        previous = current;
        current = current->next;
    }

    previous->next = new_stu;

    return 1;
}

int action_add(lianbiao* stu_lianbiao)
{
    char name[10];
    int age;
    int gender;
    int score;
    //lianbiao stu;       //这边好像不能写局部变量,栈上申请内存,感觉每次的地址是一样的额

    printf("输入需要增加(add)的数据, age=0时退出add操作:\n");
    while(1)
    {
        printf("输入姓名、年龄、性别、成绩\n");
        scanf("%s %d %d %d", name, &age, &gender, &score);
        if(age == 0)
        {
            break;
        }
        lianbiao* stu = (lianbiao*)malloc(sizeof(lianbiao));
        strcpy(stu->name, name);
        stu->age = age;
        stu->gender = gender;
        stu->score = score;
        stu->next = NULL;        //忘了写
        if(stu_add(stu_lianbiao, stu))
        {
            printf("添加成功\n");
        }
    }
    print_lianbiao(stu_lianbiao);
    printf("结束添加add操作\n");
}

//这个没有传入根指针的二级指针,竟然也成功了
//我是把根指针当做一个value=0的节点在用了,所以都是在后面add
int stu_insert(lianbiao* stu, lianbiao* new_stu)
{
    if(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = NULL;
    lianbiao* current = stu;

    while(current != NULL && current->score < new_stu->score)           //这个地方关于current->score < new_stu->score我是存在疑问的。但是实际调试的时候score的值是0.只能理解为全局变量会被初始化为0
    {
        previous = current;
        current = current->next;
    }

    if(current == NULL)                                 //这边我还是没有真正的理解书中表达的意思。这边应该写成previous == NULL
    {
        previous->next = new_stu;
    }
    else
    {
        previous->next = new_stu;
        new_stu->next = current;
    }

    return 1;
}

void action_insert(lianbiao* stu_lianbiao)
{
    char name[10];
    int age;
    int gender;
    int score;

    printf("输入需要插入(insert)的数据, age=0时退出insert操作:\n");
    while(1)
    {
        printf("输入姓名、年龄、性别、成绩\n");
        scanf("%s %d %d %d", name, &age, &gender, &score);
        if(age == 0)
        {
            break;
        }
        lianbiao* stu = (lianbiao*)malloc(sizeof(lianbiao));
        strcpy(stu->name, name);
        stu->age = age;
        stu->gender = gender;
        stu->score = score;
        stu->next = NULL;        //忘了写
        if(stu_insert(stu_lianbiao, stu))
        {
            printf("添加成功\n");
            print_lianbiao(stu_lianbiao);
        }
    }

    printf("结束添加add操作\n");
}

void stu_list_func(lianbiao* stu_lianbiao)
{
    bool flag = true;
    char choose[10];
    printf("欢迎进入学生信息管理系统\n");
    while(flag)
    {
        printf("选择操作A:增加、I:插入、D:删除、M:修改、F:查找\n");
        scanf("%s", choose);
        if(!isTargetCharacter(choose))
        {
            continue;
        }

        switch(choose[0])
        {
        case 'A':
            {
                action_add(stu_lianbiao);

                break;
            }

        case 'I':
            {
                action_insert(stu_lianbiao);
                break;
            }

        case 'D':
            {
                break;
            }

        case 'M':
            {
                break;
            }

        case 'F':
            {
                break;
            }

        case 'Q':
            {
                flag = false;
                break;
            }

        default:
            {
                break;
            }
        }
    }
}

//D:\APP\codeBlock\ADTlianbiao\main.c
//12, 7
//Hello world!
//欢迎进入学生信息管理系统
//选择操作A:增加、I:插入、D:删除、M:修改、F:查找
//A
//输入需要增加(add)的数据, age=0时退出add操作:
//输入姓名、年龄、性别、成绩
//zhangs 7 0 77
//添加成功
//输入姓名、年龄、性别、成绩
//lilu 8 1 88
//添加成功
//输入姓名、年龄、性别、成绩
//wangmei 9 0 99
//添加成功
//输入姓名、年龄、性别、成绩
//bv 0 0 0
//姓名:zhangs,年龄:7,性别:0,成绩:77
//姓名:lilu,年龄:8,性别:1,成绩:88
//姓名:wangmei,年龄:9,性别:0,成绩:99
//结束添加add操作
//选择操作A:增加、I:插入、D:删除、M:修改、F:查找
//I
//输入需要插入(insert)的数据, age=0时退出insert操作:
//输入姓名、年龄、性别、成绩
//xiaoming 6 0 66
//添加成功
//姓名:xiaoming,年龄:6,性别:0,成绩:66
//姓名:zhangs,年龄:7,性别:0,成绩:77
//姓名:lilu,年龄:8,性别:1,成绩:88
//姓名:wangmei,年龄:9,性别:0,成绩:99
//输入姓名、年龄、性别、成绩
//xiaohong 9 1 101
//添加成功
//姓名:xiaoming,年龄:6,性别:0,成绩:66
//姓名:zhangs,年龄:7,性别:0,成绩:77
//姓名:lilu,年龄:8,性别:1,成绩:88
//姓名:wangmei,年龄:9,性别:0,成绩:99
//姓名:xiaohong,年龄:9,性别:1,成绩:101
//输入姓名、年龄、性别、成绩
//xc 0 0 0
//结束添加add操作

//lianbiao.h

#ifndef LIANBIAO_H
#define LIANBIAO_H

#include "stdio.h"
#include <stdbool.h>
#include <string.h>

typedef struct Lianbiao
{
    char name[10];
    int age;
    int gender;
    int score;
    struct Lianbiao* next;
}lianbiao;

typedef struct TEST
{
    int a;
    int* p;
}Test;

void func();

void stu_list_func(lianbiao* stu_lianbiao);

#endif // LIANBIAO_H

//main.c

#include <stdio.h>
#include <stdlib.h>

#include "lianbiao.h"

lianbiao g_stu_lianbiao;

int testFun(Test* test)
{
    test->a = 12;
    int* b  = (int*)malloc(sizeof(int));
    *b = 7;
    int* p = test->p;
    p = b;      //这样写不能正常赋值

    int **q = &(test->p);       //不需要加括号,->运算符优先级高于&和*
    *q = b;     //这样写可以正常赋值
}

int main()
{
    printf("%s\n", __FILE__);

    Test test;
    testFun(&test);
    printf("%d, %d", test.a, *test.p);

    printf("Hello world!\n");
    int *p = &g_stu_lianbiao;
    stu_list_func(&g_stu_lianbiao);
    return 0;
}

看打印记录感觉代码正常运行了。实际上上面的代码还是有问题,那就是我一直都其实没有理解根节点指针(rootp)是什么意思。在上面的diamante中,我其实一直都是这么用的。那就是将根节点指针指向的value=0的节点当做跟指针在用。(实际上g_stu_lianbiao的初始化就是0)。

这也是我为什么打印的时候会跳过一个节点,写的奇奇怪怪。

lianbiao* root = stu;
lianbiao* current = root->next;

上面显示我把root->next当做current。root->score所在的节点被我跳过了。

3.2.2.4 解耦阶段二

//lianbiao.h

#ifndef LIANBIAO_H
#define LIANBIAO_H

#include "stdio.h"
#include <stdbool.h>
#include <string.h>

typedef struct Lianbiao
{
    char name[10];
    int age;
    int gender;
    int score;
    struct Lianbiao* next;
}lianbiao;

typedef struct TEST
{
    int a;
    int* p;
}Test;

void func();

void stu_list_func(lianbiao** stu_lianbiao);

#endif // LIANBIAO_H

//lianbiao.c

#include "lianbiao.h"

void func()
{
    printf("hello\n");
}

bool isTargetCharacter(char* choose)
{
    if(choose[0] == 'A' || choose[0] == 'I' || choose[0] == 'D' || choose[0] == 'M' || choose[0] == 'F')
    {
        return true;
    }

    return false;
}

//这边printf也是有问题的,我也是把跟指针当做value=0在用,从第二个节点开始打印。同理,我的add函数也是等于是从value=0结构后面加的
void print_lianbiao1(lianbiao* stu)
{
    lianbiao* root = stu;
    lianbiao* current = root->next;
    while(current != NULL)
    {
        printf("姓名:%s,年龄:%d,性别:%d,成绩:%d\n", current->name, current->age, current->gender, current->score);
        current = current->next;
    }
}

void print_lianbiao(lianbiao** stu)
{
    lianbiao* current = *stu;
    while(current != NULL)
    {
        printf("姓名:%s,年龄:%d,性别:%d,成绩:%d\n", current->name, current->age, current->gender, current->score);
        current = current->next;
    }
}

int stu_add1(lianbiao* stu, lianbiao* new_stu)
{
    while(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = stu;
    lianbiao** current = &previous->next;

    while(*current != NULL)
    {
        *current = (*current)->next;
    }

    *current = new_stu;
    return 1;
}

int stu_add2(lianbiao* stu, lianbiao* new_stu)
{
    while(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = stu;
    lianbiao* current = previous->next;

    while(current != NULL)
    {
        current = current->next;
    }

    lianbiao** p = &current;
    *p = new_stu;
    return 1;
}

//吊毛,槽,这样写竟然就可以了,妈的,自己捣鼓一晚上都没弄好。看了书就好了
int stu_add3(lianbiao* stu, lianbiao* new_stu)
{
    while(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = stu;           //stu是二级指针,解引用一次,里面的成员都可以被成功赋值
    lianbiao* current = previous->next;     //现在复盘,大概是previous->next可以被赋值。但是现在的操作是previous->next赋值给previous,再去更改previous的值就更改不了next的值

    while(current != NULL)
    {
        previous = current;
        current = current->next;
    }

    previous->next = new_stu;

    return 1;
}

int stu_add(lianbiao** stu, lianbiao* new_stu)
{
    while(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = NULL;           //stu是二级指针,解引用一次,里面的成员都可以被成功赋值
    lianbiao* current = *stu;     //现在复盘,大概是previous->next可以被赋值。但是现在的操作是previous->next赋值给previous,再去更改previous的值就更改不了next的值

    while(current != NULL)
    {
        previous = current;
        current = current->next;
    }

    if(previous == NULL)
    {
        *stu = new_stu;
    }
    else
    {
        previous->next = new_stu;
        new_stu->next = current;
    }

    return 1;
}

int action_add(lianbiao** stu_lianbiao)
{
    char name[10];
    int age;
    int gender;
    int score;
    //lianbiao stu;       //这边好像不能写局部变量,栈上申请内存,感觉每次的地址是一样的额

    printf("输入需要增加(add)的数据, age=0时退出add操作:\n");
    while(1)
    {
        printf("输入姓名、年龄、性别、成绩\n");
        scanf("%s %d %d %d", name, &age, &gender, &score);
        if(age == 0)
        {
            break;
        }
        lianbiao* stu = (lianbiao*)malloc(sizeof(lianbiao));
        strcpy(stu->name, name);
        stu->age = age;
        stu->gender = gender;
        stu->score = score;
        stu->next = NULL;        //忘了写
        if(stu_add(stu_lianbiao, stu))
        {
            printf("添加成功\n");
            print_lianbiao(stu_lianbiao);
        }
    }

    printf("结束添加add操作\n");
}

//这个没有传入根指针的二级指针,竟然也成功了
//我是把根指针当做一个value=0的节点在用了,所以都是在后面add
int stu_insert1(lianbiao* stu, lianbiao* new_stu)
{
    if(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = NULL;
    lianbiao* current = stu;

    while(current != NULL && current->score < new_stu->score)           //这个地方关于current->score < new_stu->score我是存在疑问的。但是实际调试的时候score的值是0.只能理解为全局变量会被初始化为0
    {
        previous = current;
        current = current->next;
    }

    if(current == NULL)                                 //这边我还是没有真正的理解书中表达的意思。这边应该写成previous == NULL
    {
        previous->next = new_stu;
    }
    else
    {
        previous->next = new_stu;
        new_stu->next = current;
    }

    return 1;
}

int stu_insert(lianbiao** stu, lianbiao* new_stu)
{
    if(stu == NULL)
    {
        return 0;
    }

    lianbiao* previous = NULL;
    lianbiao* current = *stu;

    while(current != NULL && current->score < new_stu->score)           //这个地方关于current->score < new_stu->score我是存在疑问的。但是实际调试的时候score的值是0.只能理解为全局变量会被初始化为0
    {
        previous = current;
        current = current->next;
    }

    if(previous == NULL)                                 //这边我还是没有真正的理解书中表达的意思。这边应该写成previous == NULL
    {
        *stu = new_stu;
    }
    else
    {
        previous->next = new_stu;

    }
    new_stu->next = current;            //靠这个一直没在意,调试的时候才发现

    return 1;
}

void action_insert(lianbiao* stu_lianbiao)
{
    char name[10];
    int age;
    int gender;
    int score;

    printf("输入需要插入(insert)的数据, age=0时退出insert操作:\n");
    while(1)
    {
        printf("输入姓名、年龄、性别、成绩\n");
        scanf("%s %d %d %d", name, &age, &gender, &score);
        if(age == 0)
        {
            break;
        }
        lianbiao* stu = (lianbiao*)malloc(sizeof(lianbiao));
        strcpy(stu->name, name);
        stu->age = age;
        stu->gender = gender;
        stu->score = score;
        stu->next = NULL;        //忘了写
        if(stu_insert(stu_lianbiao, stu))
        {
            printf("添加成功\n");
            print_lianbiao(stu_lianbiao);
        }
    }

    printf("结束添加add操作\n");
}

void stu_list_func(lianbiao** stu_lianbiao)
{
    bool flag = true;
    char choose[10];
    printf("欢迎进入学生信息管理系统\n");
    while(flag)
    {
        printf("选择操作A:增加、I:插入、D:删除、M:修改、F:查找\n");
        scanf("%s", choose);
        if(!isTargetCharacter(choose))
        {
            continue;
        }

        switch(choose[0])
        {
        case 'A':
            {
                action_add(stu_lianbiao);

                break;
            }

        case 'I':
            {
                action_insert(stu_lianbiao);
                break;
            }

        case 'D':
            {
                break;
            }

        case 'M':
            {
                break;
            }

        case 'F':
            {
                break;
            }

        case 'Q':
            {
                flag = false;
                break;
            }

        default:
            {
                break;
            }
        }
    }
}

//D:\APP\codeBlock\ADTlianbiao\main.c
//12, 7
//Hello world!
//欢迎进入学生信息管理系统
//选择操作A:增加、I:插入、D:删除、M:修改、F:查找
//A
//输入需要增加(add)的数据, age=0时退出add操作:
//输入姓名、年龄、性别、成绩
//zhangs 7 0 77
//添加成功
//姓名:zhangs,年龄:7,性别:0,成绩:77
//输入姓名、年龄、性别、成绩
//lilu 8 1 88
//添加成功
//姓名:zhangs,年龄:7,性别:0,成绩:77
//姓名:lilu,年龄:8,性别:1,成绩:88
//输入姓名、年龄、性别、成绩
//wangmei 9 0 99
//添加成功
//姓名:zhangs,年龄:7,性别:0,成绩:77
//姓名:lilu,年龄:8,性别:1,成绩:88
//姓名:wangmei,年龄:9,性别:0,成绩:99
//输入姓名、年龄、性别、成绩
//vc 0 0 0
//结束添加add操作
//选择操作A:增加、I:插入、D:删除、M:修改、F:查找
//I
//输入需要插入(insert)的数据, age=0时退出insert操作:
//输入姓名、年龄、性别、成绩
//xiaoming 6 0 66
//添加成功
//姓名:xiaoming,年龄:6,性别:0,成绩:66
//姓名:zhangs,年龄:7,性别:0,成绩:77
//姓名:lilu,年龄:8,性别:1,成绩:88
//姓名:wangmei,年龄:9,性别:0,成绩:99
//输入姓名、年龄、性别、成绩
//hanxue 9 1 102
//添加成功
//姓名:xiaoming,年龄:6,性别:0,成绩:66
//姓名:zhangs,年龄:7,性别:0,成绩:77
//姓名:lilu,年龄:8,性别:1,成绩:88
//姓名:wangmei,年龄:9,性别:0,成绩:99
//姓名:hanxue,年龄:9,性别:1,成绩:102
//输入姓名、年龄、性别、成绩
//wuhu 7 0 82
//添加成功
//姓名:xiaoming,年龄:6,性别:0,成绩:66
//姓名:zhangs,年龄:7,性别:0,成绩:77
//姓名:wuhu,年龄:7,性别:0,成绩:82
//姓名:lilu,年龄:8,性别:1,成绩:88
//姓名:wangmei,年龄:9,性别:0,成绩:99
//姓名:hanxue,年龄:9,性别:1,成绩:102
//输入姓名、年龄、性别、成绩
//vc 0 0 0
//结束添加add操作
//选择操作A:增加、I:插入、D:删除、M:修改、F:查找

//main.c

#include <stdio.h>
#include <stdlib.h>

#include "lianbiao.h"

lianbiao* g_stu_lianbiao = NULL;

int testFun(Test* test)
{
    test->a = 12;
    int* b  = (int*)malloc(sizeof(int));
    *b = 7;
    int* p = test->p;
    p = b;      //这样写不能正常赋值

//    int **q = &(test->p);       //不需要加括号,->运算符优先级高于&和*
//    *q = b;     //这样写可以正常赋值

    test->p = b;        //离谱的是,这样也可以更改结构体中指针的值
}

//这里的问题是test是个指针,指向一块内存。test指针指向的内存中的数据可以更改。因为确实是对真实内存进行修改
//链表中的问题是,更改根节点的值。指针的值是值传递,无法修改。所以要传递地址的地址

int main()
{
    printf("%s\n", __FILE__);

    Test test;
    testFun(&test);
    printf("%d, %d\n", test.a, *test.p);

    printf("Hello world!\n");
    int *p = &g_stu_lianbiao;
    stu_list_func(&g_stu_lianbiao);
    return 0;
}

上面的代码就实现了C和指针里面的链表。

2024年1月17日09:36:36

今天还回顾了一下链表,理了一下,下面的是画的图

所以对malloc也理解了一些。就是malloc申请了一块内存空间。用一个指针p指向那块内存。 

四、编译报错处理

4.1 报错1

implicit declaration of function 'toupper' [-Wimplicit-function-declaration]

加上头文件"ctype.h"就不报错了。但是按理没有加头文件程序不能执行啊。但是没有头文件程序也是正常执行了。

翻译:函数'toupper'的隐式声明


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值