当前位置: 首页 > news >正文

C语言-数据结构-单链表程序-增删改查

今天学习内容是关于C语言数据结构的单链表程序,其中涉及了结构体、malloc动态分配地址的操作。

首先我们根据程序需要,写出头文件

#define _CRT_SECURE_NO_WARNINGS 1//使scanf函数生效
#include <stdio.h>//标准输入输出函数
#include <stdlib.h>//malloc动态地址分配函数

//原本是使用malloc.h来作为malloc()函数的头文件,但是后续发现malloc.h是非标准扩展,主要在某些Unix/Linux系统中存在,不是C标准的一部分,因此采用C标准库头文件stdlib.h,其中包含了malloc,calloc,realloc,free等内存管理函数。

  • 为什么推荐使用 stdlib.h

  • 标准兼容性 - 符合C标准,在所有编译器上都能工作

  • 可移植性 - 代码可以在不同平台间移植

  • 未来兼容 - 不会被新编译器淘汰


接下来我们回归正题,在完成头文件的选择后,需要使用#define宏定义两个数,用于后续的使用,这里无需深刻理解,等在程序中遇到的时候再回来看

#define NULL 0
#define LEN sizeof(struct student)int n;//用于计数,当创建一个新的结点时加一

接下来是定义一个结构体student,该结构体包含了num学号,score分数,struct student*next下一个结构体的存储地址。

struct student
{int num;float score;struct student* next;
};

接下来是创建链表的函数creat(),在本函数中创建了head、p1、p2三个结构体指针,其中head表示头结点,而p1作为缓冲指针,接收键盘输入的新数据,然后通过p2连接到链表中。(第一个结点连接的时候head和p2两个指针所指向的结构体是同一个,因此p2->next等同于head->next,并且在后续,p2->next能够重复使用,用于连接下一个新的结点)

模糊点:struct student*的使用原因:函数要返回什么类型的数据,就用什么类型来定义函数的返回类型

struct student* creat()
{struct student* head, * p1, * p2;n = 0;head = NULL;p1 = p2 = (struct student*)malloc(LEN);//注意此处的p1和p2结构体指针指向同一个结构体scanf_s("%d%f", &p1->num, &p1->score);while (p1->num != 0){n++;//标识为第n个结点if (n == 1)//当链表初次创建时,n=1,将结构体指针p1的地址赋值给结构体指针headhead = p1;else//当链表已经存在一个结点,p2的指向始终是新增结点p1的前一位p2->next = p1;//因为前两行head=p1;并且p1和p2的地址相同,因此此处的p2->next等同于head->next;p2 = p1;//此处将p2指针更新成新增的结构体地址,而p1在下一行中要被重新分配p1 = (struct student*)malloc(LEN);scanf_s("%d%f", &p1->num, &p1->score);//若%p1->num为0,当回车按下时,循环会结束,将p2的next设置为空,刚输入未接入链表的p1会被释放内存空间}p2->next = NULL;free(p1);return head;//返回头结点的地址
}

开辟一个新的地址存储结构体变量,并且p1和p2指向相同

p1的用法是代表最新结点,p2的用法是代表上一个结点,因此p1和p2的地址在while中总有相同的时候,只不过他们一相同,p1就会指向新的地址

通俗理解就是,p2是p1的专属摄影师,p1走到哪,p2就会给p1拍一张照片,在p1走到下一个地点时,p2会先在刚拍好的照片中写下下一个地点(p2->next)


那么,当你了解如何创建链表,并且能够不看示例,自己写出创建链表的函数之后,删除结点和添加结点的方法就无师自通了,接下来我就不再继续详细讲解删除结点和添加结点的操作了。

文章的最后有全部代码


接下来是删除结点

struct student* del(struct student* head, int num)
{struct student* p1, * p2 = NULL;  // 初始化 p2 为 NULLif (head == NULL){printf("链表为空!\n");return head;}p1 = head;// 遍历链表查找要删除的节点while (p1 != NULL && p1->num != num){p2 = p1;      // p2 始终指向 p1 的前一个节点p1 = p1->next;}if (p1 == NULL)//这个就是上一程序中while循环里的p1!=NULL的情况,意思是链表已经查询到最后一个NULL结点了,还没找到需要查找的学号,就会报告错误并结束程序{printf("未找到学号为 %d 的节点\n", num);return head;}// 找到要删除的节点if (p2 == NULL)   // 删除的是头节点{head = p1->next;}else              // 删除的是中间或尾节点{p2->next = p1->next;}free(p1);n--;printf("删除成功!\n");return head;
}


接下来是插入结点(按学号顺序插入)

struct student* insert(struct student* head, struct student* stu)
{struct student* p0, * p1, * p2;p0 = stu;                   // 要插入的新节点p1 = head;                  // 从头部开始查找p2 = NULL;if (head == NULL)            // 空链表情况{head = p0;p0->next = NULL;n++;return head;}// 查找插入位置(按学号升序)while ((p1->num < p0->num) && (p1->next != NULL)){p2 = p1;                // p2记录前一个节点p1 = p1->next;          // p1指向下一个节点}if (p0->num <= p1->num)      // 找到插入位置{if (head == p1)          // 插入到链表头部head = p0;else                    // 插入到p2和p1之间p2->next = p0;p0->next = p1;          // 新节点指向p1n++;                    // 节点数加1}else                        // 插入到链表尾部{p1->next = p0;p0->next = NULL;n++;}return head;
}


接下来是输出函数

// 输出链表
void print(struct student* head)
{struct student* p;printf("\n现在共有 %d 条记录:\n", n);p = head;if (head != NULL){do{printf("学号:%d,成绩:%.1f\n", p->num, p->score);p = p->next;} while (p != NULL);//在creat()函数结尾有令最后一个结点的next为空p2->next = NULL;}else{printf("链表为空!\n");}
}

接下来是主函数

int main()
{struct student* p = NULL;struct student a, * p0 = &a;int xh;printf("请输入学号和成绩,直到学号输入0为止:\n");p = creat();//return head;print(p);printf("请输入待删除的学号:\n");scanf_s("%d", &xh);p = del(p, xh);//return head;print(p);printf("请输入待插入的学号和成绩:\n");scanf_s("%d%f", &p0->num, &p0->score);p = insert(p, p0);//return head;print(p);return 0;
}

接下来是完整代码示范

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>#define NULL 0
#define LEN sizeof(struct student)int n;struct student
{int num;float score;struct student* next;
};// 创建链表
struct student* creat()
{struct student* head, * p1, * p2;n = 0;head = NULL;p1 = p2 = (struct student*)malloc(LEN);scanf_s("%d%f", &p1->num, &p1->score);while (p1->num != 0){n++;if (n == 1)head = p1;elsep2->next = p1;p2 = p1;p1 = (struct student*)malloc(LEN);scanf_s("%d%f", &p1->num, &p1->score);}p2->next = NULL;free(p1);return head;
}// 删除节点 - 修复版本
struct student* del(struct student* head, int num)
{struct student* p1, * p2 = NULL;  // 初始化 p2 为 NULLif (head == NULL){printf("链表为空!\n");return head;}p1 = head;// 遍历链表查找要删除的节点while (p1 != NULL && p1->num != num){p2 = p1;      // p2 始终指向 p1 的前一个节点p1 = p1->next;}if (p1 == NULL){printf("未找到学号为 %d 的节点\n", num);return head;}// 找到要删除的节点if (p2 == NULL)   // 删除的是头节点{head = p1->next;}else              // 删除的是中间或尾节点{p2->next = p1->next;}free(p1);n--;printf("删除成功!\n");return head;
}// 插入节点(按学号顺序插入)
struct student* insert(struct student* head, struct student* stu)
{struct student* p0, * p1, * p2;p0 = stu;                   // 要插入的新节点p1 = head;                  // 从头部开始查找p2 = NULL;if (head == NULL)            // 空链表情况{head = p0;p0->next = NULL;n++;return head;}// 查找插入位置(按学号升序)while ((p1->num < p0->num) && (p1->next != NULL)){p2 = p1;                // p2记录前一个节点p1 = p1->next;          // p1指向下一个节点}if (p0->num <= p1->num)      // 找到插入位置{if (head == p1)          // 插入到链表头部head = p0;else                    // 插入到p2和p1之间p2->next = p0;p0->next = p1;          // 新节点指向p1n++;                    // 节点数加1}else                        // 插入到链表尾部{p1->next = p0;p0->next = NULL;n++;}return head;
}
// 输出链表
void print(struct student* head)
{struct student* p;printf("\n现在共有 %d 条记录:\n", n);p = head;if (head != NULL){do{printf("学号:%d,成绩:%.1f\n", p->num, p->score);p = p->next;} while (p != NULL);//在creat()函数结尾有令最后一个结点的next为空p2->next = NULL;}else{printf("链表为空!\n");}
}// 主函数
int main()
{struct student* p = NULL;struct student a, * p0 = &a;int xh;printf("请输入学号和成绩,直到学号输入0为止:\n");p = creat();//return head;print(p);printf("请输入待删除的学号:\n");scanf_s("%d", &xh);p = del(p, xh);//return head;print(p);printf("请输入待插入的学号和成绩:\n");scanf_s("%d%f", &p0->num, &p0->score);p = insert(p, p0);//return head;print(p);return 0;
}

http://www.dtcms.com/a/613801.html

相关文章:

  • vip广告网站建设摄影网站开题报告
  • 进程概念(上)
  • 网络水果有哪些网站可以做中国国家人事人才培训网
  • 开启智能未来之门:华为HCIA-AI认证培训与考试全方位深度解析
  • 记事本源代码分析ALT+F4调试记录详细分析
  • 【Java基础07】链表
  • DDL数据
  • 北京驾校网站建设方一凡和磊儿做家教的网站
  • 电的帝国与时空的编程:从基础属性到人工场革命的宏伟蓝图
  • C语言入门(十七):指针(3)
  • 共绩算力全面研究报告:破解算力 “不可能三角“ 的创新实践
  • 网络:5.应用层协议HTTP
  • python 要如何快速拥有可用python的编程能力
  • 网站跳出率房地产的未来趋势分析
  • 家庭网络建站广告设计公司服务不到位
  • SAP FICO工单成本分析报表
  • 破解版网站建设营销运营推广服务
  • 告别“在我电脑上能跑”:Docker入门与核心概念解析
  • 2. YOLOv5 搭建一个完整的目标检测系统核心步骤
  • discuz培训网站模板下载wordpress直接显示文章
  • R语言编程基础与应用 | 探索数据分析的无限可能
  • 北京网站设计网站设计公司价格工商做年报网站
  • 商丘网站建设网站推广镇江做网站的
  • Nginx安全策略
  • NumPy 从数值范围创建数组
  • 网站备案贵州电话学产品设计的可以找什么工作
  • 刷网站排名 优帮云企业所得税什么时候申报缴纳
  • P5736 【深基7.例2】质数筛题解(重置版!)
  • Qt--通过JLinkARM.dll实现Jlink自动烧写
  • gitlab cicd 模块解释