首页 > 其他分享 >2024-02-19-物联网C语言(9-链表)

2024-02-19-物联网C语言(9-链表)

时间:2024-02-20 14:00:11浏览次数:20  
标签:02 pb head 19 next 链表 new 节点

9.链表

9.1 概念

假如:做一个班级信息管理系统,统计班级学生的信息而我们事先不知道班级人数,或者知道人数,但是中间人员可能发生变化:比如有新同学加入,有同学请假,又或者我们需要统计班级的平均成绩等等

目标:要做一个类似 QQ、飞秋类似的通信软件,其中有一个功能,类似用户上下线检测、有新的用户上线、下线实时更新显示,可以实时查询在线状态、按姓名排序等以上问题如何使用学过的C语言知识处理呢?

使用数组远远不能达到我们的要求,因为数组只能存储同类型的数据,这个场景需要的是多种类型的数据存储。

  1. 数组不能实现动态的内存申请;
  2. malloc申请的空间也不能实现局部申请和释放

这就要链表登场了

定义: 链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。

img

9.1.1 链表的构成

特点: 链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(malloc),每个节点包括两个部分:

  1. 一个是存储数据元素的数据域
  2. 另一个是存储下一个节点地址的指针域
typedef struct student{
    int num;
    char name;    // 数据域
    struct student *next;  // 指针域,存放下一个节点的首地址
} STU;

9.2链表的操作

9.2.1 链表的创建

image-20240220085105683

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

typedef struct student
{
    int num;
    int score;
    char name[20];
    // 指针域
    struct student *next;
} STU;

void link_create_head(STU **p_head, STU *p_new)
{
    STU *p_mov = *p_head;

    if (*p_head == NULL)
    {                    // 当第一次加入链表为空
        *p_head = p_new; // 头节点保存新插入节点的地址
        p_new->next = NULL;
    }
    else // 第二次及以后加入链表
    {
        while (p_mov->next != NULL)
        {
            p_mov = p_mov->next; // 找到原有链表的最后一个节点
        }
        p_mov->next = p_new; // 将新申请的节点加入链表
        p_new->next = NULL;
    }
}

int main(int argc, char const *argv[])
{
    STU *head = NULL, *p_new = NULL;
    int num, i;
    printf("请输出链表的初始个数:\n");
    scanf("%d", &num);
    for (i = 0; i < num; i++)
    {
        p_new = (STU *)malloc(sizeof(STU)); // 申请一个新节点
        printf("请输入学号、分数、名字\n"); // 给新节点赋值
        scanf("%d %d %s", &p_new->num, &p_new->score, &p_new->name);
        link_create_head(&head, p_new); // 将新节点加入链表
    }
    return 0;
}

9.2.2 链表的遍历

image-20240220091308317

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

typedef struct student
{
    int num;
    int score;
    char name[20];
    // 指针域
    struct student *next;
} STU;

void link_create_head(STU **p_head, STU *p_new)
{
    STU *p_mov = *p_head;

    if (*p_head == NULL)
    {                    // 当第一次加入链表为空
        *p_head = p_new; // 头节点保存新插入节点的地址
        p_new->next = NULL;
    }
    else // 第二次及以后加入链表
    {
        while (p_mov->next != NULL)
        {
            p_mov = p_mov->next; // 找到原有链表的最后一个节点
        }
        p_mov->next = p_new; // 将新申请的节点加入链表
        p_new->next = NULL;
    }
}

void link_print(STU *head)
{
    STU *p_mov;
    // 定义新的指针保存链表,防止使用head改变原本链表
    p_mov = head;
    // 当循环到最后一个节点的指针域为NULL的时候,结束循环
    while (p_mov != NULL)
    {
        // 先打印当前指针保存节点的指针域
        printf("num = %d score = %d name: %s\n", p_mov->num, p_mov->score, p_mov->name);

        // 指针后移,指向下一个节点的地址
        p_mov = p_mov->next;
    }
}

int main(int argc, char const *argv[])
{
    STU *head = NULL, *p_new = NULL;
    int num, i;
    printf("请输出链表的初始个数:\n");
    scanf("%d", &num);
    for (i = 0; i < num; i++)
    {
        p_new = (STU *)malloc(sizeof(STU)); // 申请一个新节点
        printf("请输入学号、分数、名字\n"); // 给新节点赋值
        scanf("%d %d %s", &p_new->num, &p_new->score, &p_new->name);
        link_create_head(&head, p_new); // 将新节点加入链表
    }
    link_print(head);
    return 0;
}

执行输出结果

请输出链表的初始个数:
2
请输入学号、分数、名字
1 2 zhangsan
请输入学号、分数、名字
1 3 lisi 
num = 1 score = 2 name: zhangsan
num = 1 score = 3 name: lisi

9.2.3 链表的释放

image-20240220100439493

void link_free(STU **p_head){
    // 定义一个指针变量保存头结点的地址
    STU *pb = *p_head;
    while (*p_head != NULL){
        // 先保存p_head指向的结点地址
        pb = *p_head;
        // p_head 保存下一个结点的地址
        *p_head = (*p_head)->next;
        // 释放结点,防止野指针
        free(pb);
        pb = NULL;
    }
}

9.2.4 链表结点的查找

//链表的查找//按照学号查找
STU *link_search_num(STU *head,int num){
    STU *p_mov;
    //定义的指针变量保存第一个结点的地址
    p_mov=head;
    //当没有到达最后一个结点的指针域时循环继续
    while(p_mov!=NULL){
        // 对比,如果找到是当前需要的数据,则返回结点的地址
        if(p_mov->num == num){
            //找到了
            return p_mov;
        };
        p_mov=p_mov->next;
    }
    return NULL; //没有找到
}

9.2.5 链表结点的删除

  1. 如果链表为空,则不需要删除
  2. 如果删除的是第一个结点A,则需要将A的指针域保存其下一个节点B的地址
  3. 如果删除的是中间结点B,那么需要找到这个节点的前一个节点A,使前一个节点的指针域保存这个节点(B)后一个节点C的地址
void link_delete_num(STU **p_head,int num){
    STU *pb,*pf;
    pb = pf = *p_head;
    if (*p_head == NULL){ // 1. 链表为空,不用删
        printf("链表为空,没有需要删除的节点");
        return;
    }
    while (pb-> num != num && pb->next !=NULL){ // 循环找需要删除的节点
        pf = pbl;
        pb = pb->next;
    }
    if (pb->num ==num)
    { // 找到了一个节点的num和num相同
        if( pb ==*p_head){  //2. 如果是头结点,让保存头节点的指针保存后一个节点的地址
            *p_head = pb->next;
        }else{
            pf->next = pb->next; // 3. 如果是中间结点,其前一个节点指针保存其后一个节点地址
        }
        free(pb);
        pb = NULL;
    }else{
        printf("没有需要删除的节点\n");
    }

9.2.6 链表中插入节点

链表中插入节点,按照原链表顺序插入,找到合适的位置

image-20240220103519968

  1. 如果链表没有节点,则新插入的就是头结点
  2. 如果新插入的节点数值最小,则其就是头结点
  3. 如果新插入的结点数值在中间位置,则找到前一个,然后插入到他们中间
  4. 如果新插入的节点数值较大,则插入到最后
//链表的插入:按照学号的顺序插入
void link insert_num(STU **p_head,STU *p_new){
    STU *pb,*pf;
    pb=pf=*p_head;
    // 1. 链表为空链表,新插入的结点就是头结点
    if(*p_head == NULL){
        *p_head = p_new;
        p_new->next = NULL;
        return ;
    }

    while((p_new->num >= pb->num)&& (pb->next !=NULL)){ // 找到需要插入位置的前一个结点和后一个结点
        pf=pb;
        pb=pb->next;
    }
    // 2. 找到一个节点pb的num比新来的节点num大
    if (p_new->num < pb->num){
        if (pb == *p_head){
            p_new->next = *p_head; // 如果这个节点是头结点,那么新来的节点需要插到最前面,作为头结点
            *p_head = p_new;
        }else{
            pf->next = p_new; // 如果这个节点不是头节点,新来的节点插入到中间
            p_new->next = pb;
        }

    }else{ // 没有找到一个节点pb的num比新来的节点大,那么新来的节点就是最后一个结点
        pb->next = p_new;
        p_new->next = NULL;
    }
}

9.2.7 链表的排序

  1. 如果链表为空,不需要排序
  2. 如果链表只有一个结点,不需要排序
  3. 如果链表有多个节点,采用交换排序的方式。先将第一个结点与后面所有的结点依次对比数据域,只要有比第一个结点数据域小的,则交换位置交换之后,拿新的第一个结点的数据域与下一个结点再次对比,如果比他小,再次交换,依次类推第一个结点确定完毕之后,接下来再将第二个结点与后面所有的结点对比,直到最后一个结点也对比完毕为止
void link_order(STU *head){
    STU *pb,*pf,temp;
    pf = head;
    if(head == NULL){
        printf("链表为空,不用排序\n");
        return ;
    }
    if (head->next == NULL){
        printf("链表只有一个结点,不用排序\n");
        return ;
    }
    while (pf->next != NULL){
        pb = pf->next; //pd从基准元素的下个元素开始
        while (pb != NULL){  // 结点不为NULL,即不为最后一个节点
            if (pf->num > pb->num){  // 交换节点值和节点中的指针指向
                temp = *pb;
                *pb = *pf;
                *pf =temp;

                temp.next = pb ->next;
                pb->next = pf->next;
                pd->next = temp.next
            }
            pb = pb->next;
        }
        pf = pf->next;
    }
}

9.2.8 链表逆序

image-20240220110723687

//链表逆序
STU *link_reverse(STU *head){
    STU *pf,*pb,*r;
    pf=head;
    pb=pf->next;
    while(pb!=NULL){
        r=pb->next;
        pb->next=pf;
        pf=pb;
        pb=r;
    }
    head->next=NULL;
    head=pf;
    return head;
}

9.3 双向链表

image-20240220112731220

9.3.1 创建和遍历

  1. 创建一个节点为头结点,将两个指针都保存NULL
  2. 找到链表中最后一个结点A,A的指针保存新插入的结点B地址;B有两个指针域,一个保存A结点地址,另一个保存NULL
double link creat head(STU **p _head,STU *p_new){
    STU *p_mov=*p_head;
    if(*p_head==NULL){  //当第一次加入链表为空时
        *p_head= p_new;
        p_new->front = NULL;
        p_new->next = NULL;
    }else{
        //第二次及以后加入链表
        while(p_mov->next!=NULL){
            //找到原有链表的最后一个节点
            p_mov=p_mov->next;
        }
        //将新申请的节点加入链表
        p_mov->next= p_new;
        p_new->front=p_mov;
        p_new->next = NULL;
    }
}

9.3.2 结点删除

  1. 如果链表为空,则不需要删除
  2. 如果删除第一个结点A,则保存链表首地址的指针保存后一个结点B的地址,并且让结点B的front保存NULL
  3. 如果删除最后一个结点Z,只需要让最后一个结点的前一个结点Y的next保存NULL即可
  4. 如果删除中间结点M,则让中间结点的前后两个结点的指针域分别保存对方的地址即可
void doubel_link_delete_num(STU **p_head,int num){
    STU *pb,*pf;
    pb = *p_head;
    if (*p_head == NULL){ // 1. 链表为空
        printf("链表为空,没有需要删除的节点\n");
        return ;
    }
    
    while ((pb->num !=num) && (pb->next !=NULL)){  // 遍历需要删除的节点
        pb = pb->next;
    }
    if (pb->num == num){  // 找到了一个节点的num与num相同,删除pb指向的节点
        if (pb == *p_head){  // 2. 如果找到的是头节点
            if ((*p_head)->next ==NULL){
                *p_head = pb->next;
            }else{  // 如果有多个节点
                *p_head = pb->next; 
                (*p_head)->front = NULL;
            }
        }else{ //要删除的节点是其他节点
            if (pb->next !=NULL){ // 3.删除中间节点
                pf = pb->front;
                pf->next = pb->next;
                (pb->next)->front = pf;
            }else{ // 4.删除尾部节点
                pf =pb->front; 
                pf->next = NULL;
            }
            
        }
    }
}

9.3.3 插入节点

按照顺序插入节点

//链表的插入:按照学号的顺序插入
void link insert_num(STU **p_head,STU *p_new){
    STU *pb,*pf;
    pb=pf=*p_head;
    // 1. 链表为空链表,新插入的结点就是头结点
    if(*p_head == NULL){
        *p_head = p_new;
        p_new->next = NULL;
        return ;
    }

    while((p_new->num >= pb->num)&& (pb->next !=NULL)){ // 找到需要插入位置的前一个结点和后一个结点
        pf=pb;
        pb=pb->next;
    }
    // 2. 找到一个节点pb的num比新来的节点num大
    if (p_new->num < pb->num){
        if (pb == *p_head){
            p_new->next = *p_head; // 如果这个节点是头结点,那么新来的节点需要插到最前面,作为头结点
            (*p_head)->front = p_new; // 头结点的front指保存新插入的结点地址
            *p_new->front = NULL; // 新插入的节点front保存NULL
            *p_head = p_new; // 原本保存链表首地址的指针,保存新插入的节点地址
        }else{
            pf->next = p_new; // 如果这个节点不是头节点,新来的节点插入到中间
            p_new->front = pf;
            pf->next =p_new;
            pb->front = p_new;
        }

    }else{ // 没有找到一个节点pb的num比新来的节点大,那么新来的节点就是最后一个结点
        pb->next = p_new;
        p_new->front = pb;
        p_new->next = NULL
    }
}

标签:02,pb,head,19,next,链表,new,节点
From: https://www.cnblogs.com/hasaki-yasuo/p/18022939

相关文章

  • 2024全年放假日历表及调休安排 用手机便签设置放假倒计时
    对于绝大多数的上班族来说,春节长假已经结束,现在要回归到正常的工作和生活中。为了给生活增加一些“盼头”,很多小伙伴不约而同打开手机日历,查看下个法定节假日是什么时候。下面给大家具体讲一下2024全年放假日历表及调休安排!除去元旦、春节之外,清明节是4月4日至6日放假共3天,4月7日......
  • react 备忘.md.18022871
    useStateuseState是React中一个基本的钩子(Hook),用于在函数组件中添加状态。这个钩子让你能够在不编写类组件的情况下保持组件的内部状态。useCallbackuseCallback是React的一个钩子(Hook),它返回一个记忆化(memoized)的回调函数。这个钩子在某些场景下非常有用,特别是当你需要传......
  • SDNU_ACM_ICPC_2024_Winter_Practice_1st 赛后
    A:题目给出t个n,对每个n,令n=x+y+z,x|n,y|n,z|n,输出最大的xyz的值。解法打表找规律#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;intmain(){ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);intt;cin>>t;while(t--){......
  • Java实现静态链表
    本文参照了大话数据结构的静态链表的c语言实现packagecom.luoyi.list;/***@Description静态链表*@AuthorLuoyi*@Date2024/2/19**注:1.索引为0的节点不存放数据,cur指向第一个空闲节点的下标*2.最后一个元素(即下标Maxsize-1)的cur指向第一个有效数......
  • 互联网信息服务算法推荐管理规定 (全文学习)2022年01月04日 本规定自2022年3月1日起施
    互联网信息服务算法推荐管理规定第一章总则第一条 为了规范互联网信息服务算法推荐活动,弘扬社会主义核心价值观,维护国家安全和社会公共利益,保护公民、法人和其他组织的合法权益,促进互联网信息服务健康有序发展,根据《中华人民共和国网络安全法》、《中华人民共和国数据安全法......
  • 上海市促进 人工智能产业发展条例 (全文学习)2022年10月01日 本条例自2022年10月1日
    上海市促进人工智能产业发展条例(2022年9月22日上海市第十五届人民代表大会常务委员会第四十四次会议通过)第一章 总则  第一条 为了促进人工智能产业高质量发展,强化新一代人工智能科技创新策源功能,推动人工智能与经济、生活、城市治理等领域深度融合,打造人工智能世界级产业......
  • 2024年1月国产数据库大事记-墨天轮
    本文为墨天轮社区整理的2024年1月国产数据库大事件和重要产品发布消息。目录2024年1月国产数据库大事记TOP102024年1月国产数据库大事记(时间线)产品/版本发布兼容认证代表厂商大事记厂商2023年终总结合辑排行榜新增数据库厂商活动2024年1月国产数据库大事记TOP10......
  • 10.【2024初三集训模拟测试2】
    \(\Huge打了一场模拟赛,又垫底了。qwq\)2024初三集训模拟测试1不会有人比本蒟蒻更蒻了\(qwq\)。T1小P的2048\(0pts\)\(\Large⚓⚓⚓\)\(\Large......
  • 开工大吉——推荐一款2024年开发者可能会用到表格控件
    前言在现代工作环境中,信息的处理和管理是至关重要的。表格是一种常见的数据呈现和整理工具,被广泛应用于各行各业。然而,随着技术的不断发展,市场对表格控件的需求也越来越高。随着工作效率的重要性日益凸显,一款高效的表格控件成为了开发者们的首选,因此本文小编将从葡萄城公司的纯前......
  • 2024初三年后集训模拟测试2
    前言比赛链接没什么好说的,丫的为啥不用开\(freopen\),我全开了qwq。很好的全部爆零,他题面里明明要求\(freopen\),但是标准输入输出,没这个经验,长个见识吧就当。去了\(freopen\)后:\(T1\)大模拟,不好打但肯调就能出。剩下的都不是很会,打到\(T4\)愣了半天题面就结束......