C语言实现线性动态(单向)链表【详细步骤】

文章目录

  • 什么是链表
  • 为什么不用结构体数组
  • 链表的操作
    • 创建表
    • 删除元素
    • 插入元素
  • 代码及运行结果

什么是链表

链表是数据结构里面的一种,线性链表是链表的一种,线性链表的延伸有双向链表和环形链表。在编程语言中优化数据结构可以在处理大数据时大大降低程序的空间复杂性和时间复杂性。这里我只用一个简单的例子——线性单向链表为例,说明C语言是如何实现该结构的。
链表的元素是由结构体来实现struct table *p。结构体中有一个成员是结构体指针struct table *next,而这个结构体指针的类型和此结构体类型相同。除链表最后一个元素外,每一个结构体的指针都指向链表中下一个元素的结构体,最后一个元素的结构体指针为空(NULL)。保存链表时,只需要记录下链表的头指针,即链表中第一个结构体的地址即可。添加一个链表元素时,都需要单独申请一段内存;删除时则将其释放掉。在查找链表时,只需要顺着结构体指针的顺序一个一个往下查找,直到查找的结构体中的成员指针。以下是一个链表结构的示意:

struct Student{
int ID;//学号
char[20];//姓名
int marks[5];//5门考试的成绩
struct Student *next;//指向下一个结构体的结构体指针
}

为什么不用结构体数组

有人会有问为什么不直接用一个结构体数组代替链表,结构体数组占据的内存空间是连续的,如果使用malloc指令一样可以动态存储,而且连续的内存空间肯定比不确定的内存空间效果要好。但是如果对一个动态数组插入或者删除元素的话,它后面的所有元素都需要变动位置,因此修改数组会比链表要难的多。但它的优点在于查找方式很灵活,每一个元素相对于数组首元素地址都有一个偏移量i,因此对于数组来说既可以顺向查找也可以反向查找,还可以二分法查找。
由于链表的每一个元素都有一段内存,这些内存未必是连续的,加上链表本身会比结构体数组多一个指向下一个元素的结构体指针,因此从节省内存的角度看链表是不如数组的。但是链表删除元素的时候(假设这个元素是a[k])只需要a[k-1]的next指针指向a[k+1],再把a[k]的内存释放即可,非常方便;插入元素的时候(假设这个元素是a[n]),只需要a[n-1]的next指针指向a[n]再将a[n]的指针指向a[n+1]即可,相比于数组来说只修改了两个元素,速度快而且非常方便。

链表的操作

链表的操作分为——创建表、插入元素、删除元素、清空表、查找表、打印表。其中插入/删除的元素可以是一个也可以说多个。链表从存储类型上来分可以分为静态链表和动态链表,静态链表是事先编写好的链表,占用的内存是静态存储区的内存,使用时不可以对其中的元素进行删减,只能查找;动态链表是按照程序要求生成的链表,存放于动态存储区,结构比较灵活,每一个元素都占据一部分存储空间,如果要删除元素,则释放该位置的内存;如果要添加元素,则申请一个结构体内存区的内存。

创建表

创建链表需要两个指针,一个作为先行指针(*p1),开辟内存并保存结构体的值;一个作为缓存指针(*p2),保留先行指针的所有值并且将它的next指向先行指针。构建链表时,先行指针赋一个值,后行指针保存一个值并且后行指针的next指向先行指针。赋值终止时,先行指针的next指向NULL,同时将先行指针赋值给后行指针,链表即构建完毕
代码窗口可以通过键盘的"←"和"→"查看。

struct Student * input()
{struct Student *p1,*p2,*head=NULL; printf("************************动态链表实验***********************\n【输入动态链表】\n");printf("请依次输入学号	姓名  身高(cm)  体重(kg)(用空格间隔,学号输入0结束):\n");p2=p1=(struct Student *)malloc(LEN);//开辟内存 scanf("%d %s %f %f",&p1->ID,&p1->name,&p1->height,&p1->weight);if(p1->ID==0)return(head);else head=p1;while(p1->ID!=0){p2->next=p1;p2=p1;p2->BMI=(float)p2->weight/(p2->height/100)/(p2->height/100);//求BMI指数 p1=(struct Student *)malloc(LEN);//开辟内存 scanf("%d %s %f %f",&p1->ID,&p1->name,&p1->height,&p1->weight);}p2->next=NULL;return(head);//返回链表头指针 
}

删除元素

删除元素链表的第n个元素只需要将第n-1个元素的next指针指向第n+1个元素,再将第n个元素的内存释放即可,我这里是写的其中一个例子,根据关键字学号(int stdID)删除表中的某个元素,同时返回删除后的链表首地址(如果删的是第一个元素,则链表首地址会变)
代码窗口可以通过键盘的"←"和"→"查看。

struct Student *delate(struct Student *head,int stdID)
{struct Student *p1,*p2;if(head->ID==stdID){p1=head->next;free(head);return p1;}//如果删除的是第一个元素,比较特殊,需要修改头指针,其余不动//剩余几种情况都是修改next结构体指针 for(p1=head;p1!=NULL;p2=p1,p1=p1->next)//p1指针和p2指针同时查找,p1指向当前的学生,p2保指向了上一个学生 {if(p1->ID==stdID){p2->next=p1->next;//假设找到了需要删除的学生的学号,则让它上一个学生的指针指向跳过他的下一个学生 free(p1);return head; }}return NULL;//返回NULL代表没找到 
}

插入元素

插入元素的原理是,假设要在第n个元素前插入一个元素。首先判断它是不是首元素,如果是,则修改头指针指向该元素,并将该元素的next指向原来的头指针。如果不是首元素,是第k个元素之前插入一个元素,则将第k-1个元素的next指针指向插入元素(或者子表)的地址(或者头指针),将插入元素的next指针(或尾指针)指向第k个元素。本示例代码是根据一个学号(主要关键字)插入一个元素(或者子表)的函数,返回链表的首地址(因为如果在第一个元素前面插入,可能改变链表的首地址)。
代码窗口可以通过键盘的"←"和"→"查看。

struct Student *insert(struct Student *head,int stdID,struct Student *insertstd)
{struct Student *p1,*p2,*p;for(p=insertstd;p->next!=NULL;p=p->next);//找到insert链表的最后一个元素 if(head->ID==stdID){p->next=head;return insertstd;}for(p1=head;p1!=NULL;p2=p1,p1=p1->next){if(p1->ID==stdID){p2->next=insertstd;p->next=p1;return head; }}return NULL; 
}

代码及运行结果

完整代码及注释如下:
代码窗口可以通过键盘的"←"和"→"查看。

#include <stdio.h>
#include <malloc.h>
#include <stdbool.h>
#define LEN sizeof(struct Student)//定义结构体变量的大小为符号常量LEN 
struct Student{int ID;//学号 char name[20];//姓名 float height;//身高 float weight;//体重float BMI;//BMI指数,录入时不需要计算 struct Student *next;//指向下一个结构体 
};
struct Student *input();//输入函数 
void output(struct Student * head);//输出函数 
struct Student *delate(struct Student *head,int stdID);//删除一个元素,返回删除后表的头指针 
struct Student *insert(struct Student *head,int stdID,struct Student *insertstd);//返回插入元素(子表)后的头指针 
int append(struct Student *head);//插入一个链表,从input函数输入 
struct Student *isexist(struct Student *head,int stdID);
int main()
{struct Student *present;//当前链表的头指针 int choice;bool next;int stdID;/* 1:插入一个元素2:删除一个元素3:续表 4:查找表 */ printf("**********动态链表实验**********\n初始化一个链表:\n");present=input();//当前的链表指针do{printf("请选择:\n|1:插入元素(子表)\n|2:删除元素\n|3:续表\n|4:查找表\n");scanf("%d",&choice); switch(choice)	{case 1:printf("请输入插入地点的后一个同学的学号: ");scanf("%d",&stdID);if(isexist(present,stdID)==NULL){printf("该学生不存在!\n");break;//退出switch语句 }present=insert(present,stdID,input());printf("插入元素后的链表为:\n");	output(present);break;case 2:printf("请输入删除元素的学号:  ");scanf("%d",&stdID);if(isexist(present,stdID)==NULL){printf("该学生不存在!\n");break;//退出switch语句 }present=delate(present,stdID);printf("删除后的链表为:\n"); 	output(present);break;case 3:append(present);printf("续表后的链表为:\n");output(present);break;case 4:printf("当前链表为:\n"); output(present);break;}printf("是否继续(Yes:1,No:0):  ");scanf("%d",&next);fflush(stdin);}while(next);return 0;	
} 
struct Student * input()
{struct Student *p1,*p2,*head=NULL; printf("************************动态链表实验***********************\n【输入动态链表】\n");printf("请依次输入学号	姓名  身高(cm)  体重(kg)(用空格间隔,学号输入0结束):\n");p2=p1=(struct Student *)malloc(LEN);//开辟内存 scanf("%d %s %f %f",&p1->ID,&p1->name,&p1->height,&p1->weight);if(p1->ID==0)return(head);else head=p1;while(p1->ID!=0){p2->next=p1;p2=p1;p2->BMI=(float)p2->weight/(p2->height/100)/(p2->height/100);//求BMI指数 p1=(struct Student *)malloc(LEN);//开辟内存 scanf("%d %s %f %f",&p1->ID,&p1->name,&p1->height,&p1->weight);}p2->next=NULL;return(head);//返回链表头指针 
}
void output(struct Student *head)
{struct Student *p;int num=1;p=head;//将头指针地址传给p printf("【输出动态链表】\n");printf("|学号\t\t|姓名\t|身高\t|体重\t|BMI\n");while(p!=NULL){printf("%3d|%08d\t|%s\t|%5.2f\t|%5.2f\t|%lf\n",num++,p->ID,p->name,p->height,p->weight,p->BMI);p=p->next;}
}
struct Student *delate(struct Student *head,int stdID)
{struct Student *p1,*p2;if(head->ID==stdID){p1=head->next;free(head);return p1;}//如果删除的是第一个元素,比较特殊,需要修改头指针,其余不动//剩余几种情况都是修改next结构体指针 for(p1=head;p1!=NULL;p2=p1,p1=p1->next)//p1指针和p2指针同时查找,p1指向当前的学生,p2保指向了上一个学生 {if(p1->ID==stdID){p2->next=p1->next;//假设找到了需要删除的学生的学号,则让它上一个学生的指针指向跳过他的下一个学生 free(p1);return head; }}return NULL;//返回NULL代表没找到 
}
struct Student *insert(struct Student *head,int stdID,struct Student *insertstd)
{struct Student *p1,*p2,*p;for(p=insertstd;p->next!=NULL;p=p->next);//找到insert链表的最后一个元素 if(head->ID==stdID){p->next=head;return insertstd;}for(p1=head;p1!=NULL;p2=p1,p1=p1->next){if(p1->ID==stdID){p2->next=insertstd;p->next=p1;return head; }}return NULL; 
}
int append(struct Student *head)//插入一个链表,从input函数输入 
{struct Student *p;for(p=head;p->next!=NULL;p=p->next);//找到head链表的最后一个元素 p->next=input();//从input输入需要添加的元素,可以是1个或者多个return 0; 
} 
struct Student *isexist(struct Student *head,int stdID)
{struct Student *p;for(p=head;p!=NULL;p=p->next){if(p->ID==stdID){return p;}}return NULL;
}

输出效果如下图:
C语言输出动态链表结果1C语言输出动态链表结果2
希望本文对您有价值,谢谢阅读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544541.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

移动前端经验小结

1. 移动端头部标签 head meta <!DOCTYPE html> <!-- 使用 HTML5 doctype&#xff0c;不区分大小写 --> <html lang"zh-cmn-Hans"> <!-- 更加标准的 lang 属性写法 http://zhi.hu/XyIa --> <head><!-- 声明文档使用的字符编码 -->…

再见收费的Navicat!操作所有数据库靠它就够了!

为了快速管理数据库&#xff0c;我们一般都会选择一款顺手的数据库管理工具。Navicat、DataGrip虽然很好用&#xff0c;但都是收费的。今天给大家推荐一款免费、功能强大的数据库管理工具DBeaver&#xff0c;希望对大家有所帮助&#xff01;DBeaver简介 DBeaver是一款开源的数据…

查找两个字符串中相同字符串_使两个字符串相同的最低成本

查找两个字符串中相同字符串Problem statement: 问题陈述&#xff1a; Given two strings string1 and string2 find the minimum cost required to make the given two strings identical. We can delete characters from both the strings. The cost of deleting a characte…

Matlab对指定参数的曲线进行非线性拟合

Matlab拟合曲线的方式 Matlab拟合曲线的方式有很多种&#xff0c;有三次样条插值、线性插值、多项式拟合等等。多项式拟合由于函数由f(x)anxnan−1xn−1...a1xa0f(x)a_nx^na_{n-1}x^{n-1}...a_1xa_0f(x)an​xnan−1​xn−1...a1​xa0​组成&#xff0c;若采用最小二乘法拟合&a…

MyBatis原生批量插入的坑与解决方案!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;前面的文章咱们讲了 MyBatis 批量插入的 3 种方法&#xff1a;循环单次插入、MyBatis Plus 批量插入、MyBatis 原生批量插入…

Spring学习(20)--- Schema-based AOP(基于配置的AOP实现) -- 配置切入点pointcut

pointcut&#xff08;切断点&#xff09;表达式&#xff1a; execution(public * *(..)) execution(* set*(..)) execution(* com.xyz.service.AccountService.*(..)) execution(* com.xyz.service..(..)) execution(* com.xyz.service...(..)) within(com.xyz.service.*)…

系统结构图 数据结构_数据结构图简介

系统结构图 数据结构What you are going to learn? 你要学什么&#xff1f; In this article, we learn about the introduction to Graphs in Data Structure and Algorithm. 在本文中&#xff0c;我们将了解图在数据结构和算法中的介绍 。 What are the components in Gra…

Matlab仿真PID控制(带M文件、simulink截图和参数分析)

文章目录0.符号说明1.如何根据连续系统建立差分方程1.1.获取连续系统的传递函数1.2.获取离散系统的传递函数1.3.转换为差分方程2.基本PID控制原理3.比较PID输出&#xff0c;分析参数产生的影响4.改进PID算法&#xff08;遇限削弱积分法&#xff09;5.simulink仿真0.符号说明 y…

再见 Postman!Apifox 才是 YYDS!

作为开软件开发从业者&#xff0c;API 调试是必不可少的一项技能&#xff0c;在这方面 Postman 做的非常出色。但是在整个软件开发过程中&#xff0c;API 调试只是其中的一部分&#xff0c;还有很多事情 Postman 无法完成&#xff0c;或者无法高效完成&#xff0c;比如&#xf…

Python operator.not_()函数与示例

operator.not_()函数 (operator.not_() Function) operator.not_() function is a library function of operator module, it is used to perform "NOT operation" on a given value and returns True if the given value is zero or any falsy value, False, otherw…

jprofiler安装与使用

1&#xff1a; 修改/etc/profile 增加以下内容&#xff1a; JPROFILER_HOME/opt/jprofiler9/bin/linux-x64export LD_LIBRARY_PATH$LD_LIBRARY_PATH:$JPROFILER_HOME 2&#xff1a;tomcat /bin/catalina.sh 增加以下内容&#xff1a; CATALINA_OPTS"$CATALINA_OPTS -Xms12…

Matlab【可视化作图】绘制线电压相电压辅助线

目录引言绘图原理采点绘图设置坐标轴标尺引言 学习电力电子的同学可能在私下里练习的时候非常需要三相线电压和相电压的辅助线。最近我随便找了一本书把Matlab可视化编程恶补了一下&#xff0c;给大家介绍一下这个波形辅助线是怎么做的。 三相线电压辅助线就是一组相位相差60的…

SpringBoot实现Excel导入导出,好用到爆,POI可以扔掉了!

在我们平时工作中经常会遇到要操作Excel的功能&#xff0c;比如导出个用户信息或者订单信息的Excel报表。你肯定听说过POI这个东西&#xff0c;可以实现。但是POI实现的API确实很麻烦&#xff0c;它需要写那种逐行解析的代码&#xff08;类似Xml解析&#xff09;。今天给大家推…

Python datetime __str __()方法与示例

Python datetime .__ str __()方法 (Python datetime.__str__() Method) datetime.__str__() method is used to manipulate objects of datetime class of module datetime. datetime .__ str __()方法用于操作模块datetime的datetime类的对象。 It uses a datetime class o…

Facebook升级到MySQL 8.0付出的代价

近日&#xff0c;Facebook 官博公布了他们的数据库版本从 MySQL 5.6 升级到了 MySQL 8.0&#xff0c;并且在官博记录了复盘详细的升级过程。Facebook 称&#xff0c;他们最近的一次大版本升级到 MySQL 5.6 花了一年多时间才完成&#xff0c;还在 5.6 版上开发 LSM 树存储引擎&a…

Matlab制作朱利表

朱利判据 其中 {bn−kan−k−ana0∗akcn−kbn−k−bnb0∗bk...qn−kpn−k−pnp0∗pk\begin{cases} b_{n-k}a_{n-k}-\frac{a_n}{a_0}*a_k\\ c_{n-k}b_{n-k}-\frac{b_n}{b_0}*b_k\\ ...\\ q_{n-k}p_{n-k}-\frac{p_n}{p_0}*p_k \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧​bn−k​an−k​−a0…

HDOJ 1047 Integer Inquiry

JAVA睑板.... Integer Inquiry Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 12042 Accepted Submission(s): 3037Problem DescriptionOne of the first users of BITs new supercomputer was Chip Diller. …

高并发下秒杀商品,必须知道的9个细节

高并发下如何设计秒杀系统&#xff1f;这是一个高频面试题。这个问题看似简单&#xff0c;但是里面的水很深&#xff0c;它考查的是高并发场景下&#xff0c;从前端到后端多方面的知识。秒杀一般出现在商城的促销活动中&#xff0c;指定了一定数量&#xff08;比如&#xff1a;…

weakhashmap_Java WeakHashMap keySet()方法与示例

weakhashmapWeakHashMap类的keySet()方法 (WeakHashMap Class keySet() method) keySet() method is available in java.util package. keySet()方法在java.util包中可用。 keySet() method is used to retrieve all the key exists in this map to be viewed in a set. keySet…

最小拍控制系统详细解读(阶跃输入+速度输入2个案例)【Simulink仿真】

目录索引1.符号说明与结构框图2.最小拍控制系统构造原则2.1数字控制器D(z)的构造3.简单控制对象的最小拍控制器设计3.1阶跃输入3.2速度输入1.符号说明与结构框图 y(k)——系统响应输出的离散值u(k)——数字PID控制输出的离散值r(k)——期望输出的离散值&#xff08;事先已知&a…