c语言链表.docx
- 文档编号:30489552
- 上传时间:2023-08-15
- 格式:DOCX
- 页数:13
- 大小:18.60KB
c语言链表.docx
《c语言链表.docx》由会员分享,可在线阅读,更多相关《c语言链表.docx(13页珍藏版)》请在冰豆网上搜索。
c语言链表
---------
一、单链表的建立
有了动态内存分配的基础,要实现链表就不难了。
所谓链表,就是用一组任意的存储单元存储线性表元素的一种数据结构。
链表又分为单链表、双向链表和循环链表等。
我们先讲讲单链表。
所谓单链表,是指数据接点是单向排列的。
一个单链表结点,其结构类型分为两部分:
1、数据域:
用来存储本身数据
2、链域或称为指针域:
用来存储下一个结点地址或者说指向其直接后继的指针。
例:
typedefstructnode
{
charname[20];
structnode*link;
}stud;
这样就定义了一个单链表的结构,其中charname[20]是一个用来存储姓名的字符型数组,指针*link是一个用来存储其直接后继的指针。
定义好了链表的结构之后,只要在程序运行的时候爱数据域中存储适当的数据,如有后继结点,则把链域指向其直接后继,若没有,则置为NULL。
下面就来看一个建立带表头(若未说明,以下所指链表均带表头)的单链表的完整程序。
#include
#include
#defineN10/*N为人数*/
typedefstructnode
{
charname[20];
structnode*link;
}stud;
stud*creat(intn)/*建立单链表的函数,形参n为人数*/
{
stud*p,*h,*s;/**h保存表头结点的指针,*p指向当前结点的前一个结点,*s指向当前结点*/
inti;/*计数器*/
if((h=(stud*)malloc(sizeof(stud)))==NULL)/*分配空间并检测*/
{
printf("不能分配内存空间!
");
exit(0);
}
h->name[0]='\0';/*把表头结点的数据域置空*/
h->link=NULL;/*把表头结点的链域置空*/
p=h;/*p指向表头结点*/
for(i=0;i { if((s=(stud*)malloc(sizeof(stud)))==NULL)/*分配新存储空间并检测*/ { printf("不能分配内存空间! "); exit(0); } p->link=s;/*把s的地址赋给p所指向的结点的链域,这样就把p和s所指向的结点连接起来了*/ printf("请输入第%d个人的姓名",i1); scanf("%s",s->name);/*在当前结点s的数据域中存储姓名*/ s->link=NULL; p=s; } return(h); } main() { intnumber;/*保存人数的变量*/ stud*head;/*head是保存单链表的表头结点地址的指针*/ number=N; head=creat(number);/*把所新建的单链表表头地址赋给head*/ } 这样就写好了一个可以建立包含N个人姓名的单链表了。 写动态内存分配的程序应注意,请尽量对分配是否成功进行检测。 二、单链表的基本运算 建立了一个单链表之后,如果要进行一些如插入、删除等操作该怎么办? 所以还须掌握一些单链表的基本算法,来实现这些操作。 单链表的基本运算包括: 查找、插入和删除。 下面我们就一一介绍这三种基本运算的算法,并结合我们建立单链表的例子写出相应的程序。 1、查找 对单链表进行查找的思路为: 对单链表的结点依次扫描,检测其数据域是否是我们所要查好的值,若是返回该结点的指针,否则返回NULL。 因为在单链表的链域中包含了后继结点的存储地址,所以当我们实现的时候,只要知道该单链表的头指针,即可依次对每个结点的数据域进行检测。 以下是应用查找算法的一个例子: #include #include #include #defineN10 typedefstructnode { charname[20]; structnode*link; }stud; stud*creat(intn)/*建立链表的函数*/ { stud*p,*h,*s; inti; if((h=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } h->name[0]='\0'; h->link=NULL; p=h; for(i=0;i { if((s=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } p->link=s; printf("请输入第%d个人的姓名",i1); scanf("%s",s->name); s->link=NULL; p=s; } return(h); } stud*search(stud*h,char*x)/*查找链表的函数,其中h指针是链表的表头指针,x指针是要查找的人的姓名*/ { stud*p;/*当前指针,指向要与所查找的姓名比较的结点*/ char*y;/*保存结点数据域内姓名的指针*/ p=h->link; while(p! =NULL) { y=p->name; if(strcmp(y,x)==0)/*把数据域里的姓名与所要查找的姓名比较,若相同则返回0,即条件成立*/ return(p);/*返回与所要查找结点的地址*/ elsep=p->link; } if(p==NULL) printf("没有查找到该数据! "); } main() { intnumber; charfullname[20]; stud*head,*searchpoint;/*head是表头指针,searchpoint是保存符合条件的结点地址的指针*/ number=N; head=creat(number); printf("请输入你要查找的人的姓名: "); scanf("%s",fullname); searchpoint=search(head,fullname);/*调用查找函数,并把结果赋给searchpoint指针*/ } 2、插入(后插) 假设在一个单链表中存在2个连续结点p、q(其中p为q的直接前驱),若我们需要在p、q之间插入一个新结点s,那么我们必须先为s分配空间并赋值,然后使p的链域存储s的地址,s的链域存储q的地址即可。 (p->link=s;s->link=q),这样就完成了插入操作。 下例是应用插入算法的一个例子: #include #include #include #defineN10 typedefstructnode { charname[20]; structnode*link; }stud; stud*creat(intn)/*建立单链表的函数*/ { stud*p,*h,*s; inti; if((h=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } h->name[0]='\0'; h->link=NULL; p=h; for(i=0;i { if((s=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } p->link=s; printf("请输入第%d个人的姓名: ",i1); scanf("%s",s->name); s->link=NULL; p=s; } return(h); } stud*search(stud*h,char*x)/*查找函数*/ { stud*p; char*y; p=h->link; while(p! =NULL) { y=p->name; if(strcmp(y,x)==0) return(p); elsep=p->link; } if(p==NULL) printf("没有查找到该数据! "); } voidinsert(stud*p)/*插入函数,在指针p后插入*/ { charstuname[20]; stud*s;/*指针s是保存新结点地址的*/ if((s=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } printf("请输入你要插入的人的姓名: "); scanf("%s",stuname); strcpy(s->name,stuname);/*把指针stuname所指向的数组元素拷贝给新结点的数据域*/ s->link=p->link;/*把新结点的链域指向原来p结点的后继结点*/ p->link=s;/*p结点的链域指向新结点*/ } main() { intnumber; charfullname[20];/*保存输入的要查找的人的姓名*/ stud*head,*searchpoint; number=N; head=creat(number);/*建立新链表并返回表头指针*/ printf("请输入你要查找的人的姓名: "); scanf("%s",fullname); searchpoint=search(head,fullname);/*查找并返回查找到的结点指针*/ insert(searchpoint);/*调用插入函数*/ } 3、删除 假如我们已经知道了要删除的结点p的位置,那么要删除p结点时只要令p结点的前驱结点的链域由存储p结点的地址该为存储p的后继结点的地址,并回收p结点即可。 以下便是应用删除算法的实例: #include #include #include #defineN10 typedefstructnode { charname[20]; structnode*link; }stud; stud*creat(intn)/*建立新的链表的函数*/ { stud*p,*h,*s; inti; if((h=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } h->name[0]='\0'; h->link=NULL; p=h; for(i=0;i { if((s=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } p->link=s; printf("请输入第%d个人的姓名",i1); scanf("%s",s->name); s->link=NULL; p=s; } return(h); } stud*search(stud*h,char*x)/*查找函数*/ { stud*p; char*y; p=h->link; while(p! =NULL) { y=p->name; if(strcmp(y,x)==0) return(p); elsep=p->link; } if(p==NULL) printf("没有查找到该数据! "); } stud*search2(stud*h,char*x)/*另一个查找函数,返回的是上一个查找函数的直接前驱结点的指针,*/ /*h为表头指针,x为指向要查找的姓名的指针*/ /*其实此函数的算法与上面的查找算法是一样的,只是多了一个指针s,并且s总是指向指针p所指向的结点的直接前驱,*/ /*结果返回s即是要查找的结点的前一个结点*/ { stud*p,*s; char*y; p=h->link; s=h; while(p! =NULL) { y=p->name; if(strcmp(y,x)==0) return(s); else { p=p->link; s=s->link; } } if(p==NULL) printf("没有查找到该数据! "); } voiddel(stud*x,stud*y)/*删除函数,其中y为要删除的结点的指针,x为要删除的结点的前一个结点的指针*/ { stud*s; s=y; x->link=y->link; free(s); } main() { intnumber; charfullname[20]; stud*head,*searchpoint,*forepoint; number=N; head=creat(number); printf("请输入你要删除的人的姓名: "); scanf("%s",fullname); searchpoint=search(head,fullname); forepoint=search2(head,fullname); del(forepoint,searchpoint); 一、循环链表 循环链表是与单链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。 循环链表的运算与单链表的运算基本一致。 所不同的有以下几点: 1、在建立一个循环链表时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为NULL。 此种情况还使用于在最后一个结点后插入一个新的结点。 2、在判断是否到表尾时,是判断该结点链域的值是否是表头结点,当链域值等于表头指针时,说明已到表尾。 而非象单链表那样判断链域值是否为NULL。 二、双向链表 双向链表其实是单链表的改进。 当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。 这是由单链表结点的结构所限制的。 因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢? 这就是双向链表。 在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。 在c语言中双向链表结点类型可以定义为: typedefstructnode { intdata;/*数据域*/ structnode*llink,*rlink;/*链域,*llink是左链域指针,*rlink是右链域指针*/ }JD; 当然,也可以把一个双向链表构建成一个双向循环链表。 双向链表与单向链表一样,也有三种基本运算: 查找、插入和删除 假如我们已经知道了要删除的结点p的位置,那么要删除p结点时只要令p结点的前驱结点的链域由存储p结点的地址该为存储p的后继结点的地址,并回收p结点即可。 以下便是应用删除算法的实例: #include<stdio.h> #include<malloc.h> #include<string.h> #defineN10 typedefstructnode { charname[20]; structnode*link; }stud; stud*creat(intn)/*建立新的链表的函数*/ { stud*p,*h,*s; inti; if((h=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } h->name[0]='\0'; h->link=NULL; p=h; for(i=0;i<n;i++) { if((s=(stud*)malloc(sizeof(stud)))==NULL) { printf("不能分配内存空间! "); exit(0); } p->link=s; printf("请输入第%d个人的姓名",i+1); scanf("%s",s->name); s->link=NULL; p=s; } return(h); } stud*search(stud*h,char*x)/*查找函数*/ { stud*p; char*y; p=h->link; while(p! =NULL) { y=p->name; if(strcmp(y,x)==0) return(p); elsep=p->link; } if(p==NULL) printf("没有查找到该数据! "); } stud*search2(stud*h,char*x)/*另一个查找函数,返回的是上一个查找函数的直接前驱结点的指针,*/ /*h为表头指针,x为指向要查找的姓名的指针*/ /*其实此函数的算法与上面的查找算法是一样的,只是多了一个指针s,并且s总是指向指针p所指向的结点的直接前驱,*/ /*结果返回s即是要查找的结点的前一个结点*/ { stud*p,*s; char*y; p=h->link; s=h; while(p! =NULL) { y=p->name; if(strcmp(y,x)==0) return(s); else { p=p->link; s=s->link; } } if(p==NULL) printf("没有查找到该数据! "); } voiddel(stud*x,stud*y)/*删除函数,其中y为要删除的结点的指针,x为要删除的结点的前一个结点的指针*/ { stud*s; s=y; x->link=y->link; free(s); } main() { intnumber; charfullname[20]; stud*head,*searchpoint,*forepoint; number=N; head=creat(number); printf("请输入你要删除的人的姓名: "); scanf("%s",fullname); searchpoint=search(head,fullname); forepoint=search2(head,fullname); del(forepoint,searchpoint); }
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言