2024-02-19-物联网C语言(9-链表)
1.2024-02-16-物联网C语言(1-数据类型与语句)2.2024-02-17-物联网C语言(2-数组)3.2024-02-17-物联网C语言(3-函数)4.2024-02-17-物联网C语言(4-预处理)5.2024-02-17-物联网C语言(5-指针)6.2024-02-18-物联网C语言(6-动态内存申请)7.2024-02-18-物联网C语言(7-字符串处理函数)8.2024-02-18-物联网C语言(8-结构体、共用体、枚举)
9.2024-02-19-物联网C语言(9-链表)
10.2024-02-20-物联网C语言(10-文件)11.2024-02-21-物联网系统编程(1-Shell语言)12.2024-02-21-物联网系统编程(2-系统调用)13.2024-02-22-物联网系统编程(3-进程)14.2024-02-23-物联网系统编程(4-信号)15.2024-02-24-物联网系统编程(5-管道、命名管道)16.2024-02-27-物联网系统编程(6-消息队列)17.2024-02-27-物联网系统编程(7- 共享内存)18.2024-02-27-物联网系统编程(8-线程)19.2024-02-28-物联网系统编程(9-多任务互斥与同步)20.2024-02-29-Linux高级网络编程(1-计算机网络概述)21.2024-02-29-Linux高级网络编程(2-UDP编程)22.2024-02-29-Linux高级网络编程(3-UDP编程-TFTP、广播、多播)23.2024-02-29-Linux高级网络编程(4-TCP编程)24.2024-03-01-Lniux高级网络编程(5-网络通信过程)25.2024-03-01-Linux高级网络编程(6-原始套接字)9.链表
9.1 概念
假如:做一个班级信息管理系统,统计班级学生的信息而我们事先不知道班级人数,或者知道人数,但是中间人员可能发生变化:比如有新同学加入,有同学请假,又或者我们需要统计班级的平均成绩等等
目标:要做一个类似 QQ、飞秋类似的通信软件,其中有一个功能,类似用户上下线检测、有新的用户上线、下线实时更新显示,可以实时查询在线状态、按姓名排序等以上问题如何使用学过的C语言知识处理呢?
使用数组远远不能达到我们的要求,因为数组只能存储同类型的数据,这个场景需要的是多种类型的数据存储。
- 数组不能实现动态的内存申请;
- malloc申请的空间也不能实现局部申请和释放
这就要链表登场了
定义: 链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。
9.1.1 链表的构成
特点: 链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(malloc),每个节点包括两个部分:
- 一个是存储数据元素的数据域
- 另一个是存储下一个节点地址的指针域
typedef struct student{ int num; char name; // 数据域 struct student *next; // 指针域,存放下一个节点的首地址 } STU;
9.2链表的操作
9.2.1 链表的创建
#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 链表的遍历
#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 链表的释放
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 链表结点的删除
- 如果链表为空,则不需要删除
- 如果删除的是第一个结点A,则需要将A的指针域保存其下一个节点B的地址
- 如果删除的是中间结点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 链表中插入节点
链表中插入节点,按照原链表顺序插入,找到合适的位置
- 如果链表没有节点,则新插入的就是头结点
- 如果新插入的节点数值最小,则其就是头结点
- 如果新插入的结点数值在中间位置,则找到前一个,然后插入到他们中间
- 如果新插入的节点数值较大,则插入到最后
//链表的插入:按照学号的顺序插入 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 链表的排序
- 如果链表为空,不需要排序
- 如果链表只有一个结点,不需要排序
- 如果链表有多个节点,采用交换排序的方式。先将第一个结点与后面所有的结点依次对比数据域,只要有比第一个结点数据域小的,则交换位置交换之后,拿新的第一个结点的数据域与下一个结点再次对比,如果比他小,再次交换,依次类推第一个结点确定完毕之后,接下来再将第二个结点与后面所有的结点对比,直到最后一个结点也对比完毕为止
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 链表逆序
//链表逆序 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 双向链表
9.3.1 创建和遍历
- 创建一个节点为头结点,将两个指针都保存NULL
- 找到链表中最后一个结点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 结点删除
- 如果链表为空,则不需要删除
- 如果删除第一个结点A,则保存链表首地址的指针保存后一个结点B的地址,并且让结点B的front保存NULL
- 如果删除最后一个结点Z,只需要让最后一个结点的前一个结点Y的next保存NULL即可
- 如果删除中间结点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 } }
本文来自博客园,作者:Yasuo_Hasaki,转载请注明原文链接:https://www.cnblogs.com/hasaki-yasuo/p/18022939
合集:
物联网
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步