C语言——结构体链表,附完整示例

引用自身的结构体,一个结构体中有一个或多个成员的基类型就是本结构体类型时,说明这个结构体可以引用自己,所以称作引用自身的结构体。

例如下面的结构体:

struct link{ char ch; struct link *p} a;

p是一个可以指向struct link类型变量的指针成员,这样,a.p=&a就是合法的表达式。那么,这有什么意义呢?

这样的意义就是我们可以把分散存储的数据项,用一个结构体成员链接起来(当然这也耗费了那个存储指针的内存空间)看下面的程序

#include 
struct node{int data;struct node *next;
};
main(){struct node a,b,c,*p;//我们假设,这样声明的结构体变量a 、b、c在内存中并不是相临的a.data=10;  b.data=20; c.data=30; a.next=&b; b.next=&c; c.next='\0';p=&a;//结构体变量a地址赋给p while(p){printf(" %d ",p->data);//每输出一个值后,p自动指向本项的链接项 
/*这样有了一个单独保持链接的成员就把不相临的存储单元在逻辑上存储在了一起*/ p=p->next;}printf("\n");
}

这样的相链的数据存储形式称为链表!上面形成链表的方法是人为定义的,在程序执行过程中,不会生成新的存储单元,所以也称为“静态链表”

下面看一种更方法使用的“动态链表”

前面的日志中提到了C语言动态存储分配提供了“按需分配内存”的实现方法,有一个问题是,如果多次动态分配存储单元,各存储单元的地址不是一定是连续的,而所需要处理的批量数据往往是一个整体,各数据之间存在着顺序关系,这样,我们可以利用上面的链表把动态得到的存储单元在逻辑上(而不是在物理上)连接起来,就可以实现我们需要的顺序关系了。这时,是把链表技术与动态存储分配结合了起来,所以这里我们给了链表一个新的名词叫做“动态链表”

很自然,我们上面例子中的链表只有一个指向后面数据项的指针,如果有两个呢?一个指后,一个指前,这样就会出现“双向链表”与“单向链表”的区别

下面我们主要看单向链表

事实上,单身链表中的每个存储单元的数据(也就是结构体)的格式类型是相同的(包括数据成员和指针成员)

如下:struct abc{int data,……struct abc *next;};

与单向链表有关的算法有以下几类:

建立链表  输出结点数据   插入结点数据 删除结点数据

下面这个程序是示例

#include 
#include 
struct slist{ int data; struct slist *next;};
typedef struct slist SLIST;SLIST *creat_slist(){
/*该函数的作用是创建动态链表,函数的返回值是结构体指针变量,也就是新创建的动态链表的头结点,需要注意的是,这里的头结点没有数据data,只有指针next指向动态链表的第一个结点*/int c;/*用来临时存储结构体中的data*/ SLIST *h,*s,*r;/*声明工作指针*/h=(SLIST *)malloc(sizeof(SLIST));/*动态获取一个结构体的存储空间*/r=h;/*结构体指针r用来在下面的循环中使用h指针不变*/scanf("%d",&c);/*得到一个结构体成员int data数据*/ while(c!=-1){/*如果上面得到的int data数据不为-1就进入循环 */s=(SLIST *)malloc(sizeof(SLIST));/*循环中用结构体指针s获取结点*/s->data=c;/*s成员data为c注意第一次进入循环时,这个c是在循环外部上面输入的*/r->next=s;/*r指针本来与h相同,r的成员next在进入循环前是没有指向的现在进入了循环得到了一个结点,就把r的next指向新的结点,这样就使h头结点指向了s*/r=s;/*r依次与最新的结点相同,为了依次用最新的存储空间的next指向下一个获得的结点*/scanf("%d",&c);}r->next='\0';/*退出循环后,r指向最后一个结点,而这个最后结点的成员next要指向'\0'*/return h;/*返回动态链表的头结点指针*/
}
void print_slist(SLIST *head){
/*该函数是依次输出动态链表的结点,参数是结构体指针变量,也就是
动态链表的头结点*/ SLIST *p;/*声明一个工作指针,因为head不能自己往后依次移动,所以用指针p实现*/p=head->next;if(p=='\0')printf("linklist is null!\n");/*空链表*/ else{printf("head");/*非空链表*/ do{ printf("->%d",p->data);p=p->next;}while(p!='\0');printf("->end\n");}}SLIST *insert_snode(SLIST *head,int x,int y){
/*该函数实现在链表值为x的结点前面插入一个结点,值为y参数有三个链表头结点,值x,y*/ SLIST *s,*p,*q;/*定义工作指针*/s=(SLIST *)malloc(sizeof(SLIST));s->data=y;/*上面这两句先获取了一个结构体动态存储空间,并给其成员data赋值为y,但此时该空间并未成为链表的一个结点*/q=head;p=head->next;/*上面两句初始化工作指针,就是把工作指针q与head相同,p指向head的next*/while((p!='\0')&&(p->data!=x)){/*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/q=p;p=p->next;/*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/}s->next=p;/*在p指向的结点前面插入,所以新的结点的next指向p*/q->next=s;/*q-next本指向p结点,现在令其指向s结点,实现了插入*/return head;/*头指针并未变化,返回即可*/
}SLIST *insert_bnode(SLIST *head,int x,int y){
/*该函数实现在链表值为x的结点后面插入一个结点,值为y参数有三个链表头结点,值x,y*/ SLIST *s,*p,*q;/*定义工作指针*/s=(SLIST *)malloc(sizeof(SLIST));s->data=y;/*上面这两句先获取了一个结构体动态存储空间,并给其成员data赋值为y,但此时该空间并未成为链表的一个结点*/q=head;p=head->next;/*上面两句初始化工作指针,就是把工作指针q与head相同,p指向head的next*/while((p!='\0')&&(p->data!=x)){/*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/q=p;p=p->next;/*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/}s->next=p->next;/*在p指向的结点后面插入,所以新的结点的next指向p*/p->next=s;/*p-next本指向p后面的结点,现在令其指向s结点,实现了后插入*/return head;/*头指针并未变化,返回即可*/
}SLIST *del_node(SLIST *head,int x){
/*该函数实现删除链表值为x的结点,参数有两个链表头结点,值x */ SLIST *s,*p,*q;/*定义工作指针*/q=head;p=head->next;/*上面两句初始化工作指针,就是把工作指针q与head相同, p指向head的next*/while((p!='\0')&&(p->data!=x)){/*这个循环是供查找到值为x的结点所在位置的,需要注意的是这里的并的两个条件位置不能变,因为只有在p指向不为空的时候才能讨论其data成员值是否为x,否则如果p的指向是空,程序确要先判断p指向的data是不是x这样会发生地址访问错误,因为p本不指向结点,也就无从谈成员data,所以判断p指向的data是不是x是不对的*/q=p;p=p->next;/*满足循环条件的话,p与q依次后移直到找到值为x的结点或到了链表的尾部*/}q->next=p->next;/*把q->next置成p->next,*/free(p);/*释放p的存储空间,实现删除*/return head;/*头指针并未变化,返回即可*/
}
//在公众号【C语言中文社区】回复“C语言”,免费领取200G编程资料
main(){SLIST *head; int x,y;head=creat_slist();//创建链表函数print_slist(head);printf("please input x\n");  scanf("%d",&x);printf("please input y\n");  scanf("%d",&y);head=insert_snode(head,x,y);//结点前插入函数print_slist(head);printf("please input x\n");  scanf("%d",&x);printf("please input y\n");   scanf("%d",&y);head=insert_bnode(head,x,y);//结点后插入函数print_slist(head);printf("please input x\n"); scanf("%d",&x);head=del_node(head,x);//删除结点函数print_slist(head);
}

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

9b8c89bcef4f3c296bc838036114599c.png

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

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

相关文章

android 注解点击事件,android click事件注解

定义注解:package com.fyfeng.android.annotations;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;Retention(value RetentionPolicy.RUNTIME…

jax-rs jax-ws_快速浏览JAX-RS请求与方法匹配

jax-rs jax-ws在本文中,我们来看一下JAX-RS中与资源方法匹配的HTTP请求 。 它是JAX-RS的最基本功能之一。 通常,使用JAX-RS API的开发人员不会接触(或真正不需要知道) 匹配过程的细节,请放心,由于我们的RES…

C语言知识总结——宏,枚举

1、define宏定义以#号开头的都是编译预处理指令,它们不是C语言的成分,但是C程序离不开它们,#define用来定义一个宏,程序在预处理阶段将用define定义的来内容进行了替换。因此在程序运行时,常量表中并没有用define定义的…

C语言知识总结——共用体

union 共用体(联合体)在进行某些算法的C语言编程的时候,需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术,几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构,在C语言中 以关键字union…

jboss入门_JBoss Forge NetBeans集成–入门

jboss入门JBoss Forge是构建基于Maven的Java EE项目的最快方法。 因此,它已经具有了令人敬畏的功能,使您作为开发人员的生活更加轻松。 在大多数情况下,使用Forge的人们可能会对创建Web应用程序感兴趣。 有很多方法可以开始使用Forge基础知识…

这几道 C/C 题涉及你的知识盲区?

8个C语言面试题,涉及指针、运算、函数、内存,看看你能做出几个!1.gets()函数问:请找出下面代码里的问题:#include int main(void) {char buff[10];memset(buff, 0, sizeof(buff));gets(buff); //gets不检查输入的字符…

logback redis_使用Spring Boot和Logback登录到Redis

logback redis在进行集中式日志记录时,例如使用Elasticsearch,Logstash和Kibana或Graylog2,您可以为Java应用程序提供多个选项。 您既可以编写标准的应用程序日志,也可以使用Logstash解析这些日志,这些日志既可以直接使…

荣耀6plus+android5.1,荣耀66Plus EMUI3.0开发版5.5.1版本发布说明

本帖最后由 秀姬 于 2015-6-8 11:22 编辑EMUI 3.0_Android4.4_5.5.1版本发布说明(开发版)适配机型:荣耀6 / 荣耀6 Plus 全系列机型下载地址:荣耀6标配版支持荣耀6手机全系列机型,不区分制式!标配版支持L0X的各机型升级&#xff0c…

C 与 C 的真正区别在哪里?

C 与 C 的真正区别在哪里?C是中餐厨师的菜刀,做啥菜就那一把刀,切菜切肉切鱼,都是这一把刀,刀工好的师傅,豆腐都能切成一朵花。无论你提什么概念,都能用指针给你做出来,如果不行&…

byteman_Byteman –用于字节码操纵的瑞士军刀

byteman我正在与JBoss中的许多社区一起工作,有很多有趣的事情要谈论,以至于我无法将自己的每一分都缠住。 这就是为什么我非常感谢有机会不时地欢迎客座博客的主要原因。 今天是Jochen Mader,他是以代码为中心的书呆子群的一部分。 他目前花费…

html 怎么置顶表格,表格(Table)表头固定,内容上滚【5个实例】

当表格往上滚动的时候,表头固定不动,这样可以让用户时刻看清每一列的数据含义,这是人性化的设计,充分考虑了用户使用体验。本文将通过5个实例,来介绍这种表格设计。用户可通过下载源码,直接应用于自己的项目…

C语言变量的定义包括变量存储类型和变量的什么?

C语言变量的定义包括变量存储类型和变量的名称。C语言定义变量的格式:“数据类型 变量名;”,“数据类型”表示想要存储什么类型的数据,“变量名”就是你想给这个变量起个什么名字,通常都是用字母。变量的定义定义变量的格式非常简…

java 调用祖父方法_在Java中调用祖父母方法:您不能

java 调用祖父方法在文章保护的重点中,我详细介绍了“受保护”如何扩展“包私有”访问。 我在那儿写道: 你能做的是 覆盖子类中的方法或 使用关键字super调用parent方法。 通常,这实际上是您可以使用受保护的方法完成的所有操作。 &…

C语言代码注释必须用/**/ , 你没看错~

事情是这样的,有人离职,公司调我补缺。那个系统一直有个工程师在维护,参与该系统的新人来了又走,他始终泰然自若。刚过去一个礼拜,我就心下窃吼:“坑爹啊!”,也彻底体会到什么叫---绝对的权威、…

java8 streams_另一个Java 8 Lamdbas和Streams示例

java8 streams我一直落后于Java 8所关注的功能,因此在这篇文章中,我将简要介绍我对lambda和stream的初步经验。 和往常一样,我将专注于Podcast课程: package org.codingpedia.learning.java.core;import java.util.Comparator;p…

html文档的基本类型,HTML(网页的文档类型介绍)

一个html文件的第一行代码通常就是用于声明网页文档类型,其格式是:这一行不是属于标签文档类型:可以理解为不同的html版本!html4.0 或4.01版本基本固定,但又有分化:严格性:了用的标签和属性相对较少,但能兼容更多的浏览器。宽松型…

C语言源代码展示:常用转换函数实现原理

编程时经常用到进制转换、字符转换。比如软件界面输入的数字字符串,如何将字符串处理成数字呢?和大家分享一下。01字符串转十六进制代码实现:void StrToHex(char *pbDest, char *pbSrc, int nLen) {char h1,h2;char s1,s2;int i;for (i0; i …

jax-rs jax-ws_在JAX-RS中处理异步请求中的超时

jax-rs jax-wsJAX-RS 2.0在客户端和服务器端都支持异步编程范例。 这篇文章重点介绍了使用JAX-RS(2.0)API在服务器端执行异步REST请求时的超时功能 无需过多介绍,这里是一个快速概述。 为了以异步方式执行方法,您只需 需要指定A…

html5 移动 优化,第四天:HTML5移动站优化技巧 摘自《10天学会移动站SEO》

现在大家基本上做手机网站都是做成HTML5的,因为现在智能手机等移动设备越来越多,几乎全部支持HTML5,那么给网站适配上HTML5的网站就很是必要了。以前的WML网站已经淘汰,而最新的方式就这种最好。我们这一节就重点讲一讲HTML5移动网…

c语言中switch的用法是什么?

c语言中switch的用法是:功能:switch语句是多分支选择语句.用来实现多分支选择结构.if语句只有两个分支可供选择,而实际问题中常常要用到多分支的选择.例如,学生成绩分类(90为"A"等,80-89分为B等,70-90分为C等......).当然这些都可以用嵌套的if…