关于单链表的操作

  • 格式:doc
  • 大小:62.00 KB
  • 文档页数:7

关于单链表的操作单向链表结构描述示例:struct linklist{ int data;struct linklist *next;};struct linklist *head;单链表的建立在链表的操作中用到的几个函数:(1)malloc(size)在内存的动态存储区申请一个长度为size字节的连续空间。

并将此存储空间的起始地址作为函数值返回。

malloc函数的原型为: void *malloc(unsigned int size)函数值为指针(地址),这个指针是指向void类型的,也就是不规定指向任何具体的类型。

如果内存缺乏足够大的空间进行分配,则malloc的函数值为“空指针”。

(2)calloc(n, size)在内存的动态存储区申请n个长度为size 字节的连续空间,函数返回值为分配空间的首地址。

若此函数未被成功执行,函数返回值为0。

函数返回值为该空间的首地址。

其函数原型为:void *calloc(unsigned int n,unsigned int size)(3)free(p)释放由指针p所指向的存储单元,而所释放的存储单元的大小是最近一次调用malloc()函数或calloc()函数时所申请的存储空间。

free 函数的原型为:void free(void *ptr)系统可以回收所释放的空间,另行分配作为它用。

(4)realloc函数。

其函数的原型为:void realloc(void *ptr,unsigned int size)其作用是将ptr所指向的存储区(原先用malloc函数分配的)的大小改为size个字节。

可以使原先分配区扩大和缩小。

它的函数返回值是一个指针,即新的存储区的首地址。

例1 使用表首插入法建立链表。

表首插入法建立链表,这种方法的特点是:新产生的结点作为新的表头插入链表。

#include <stdio.h>void main(){struct linklist{ int data;struct linklist *next;};struct linklist *head,*p; /*head头指针,p申请单元指针*/head=NULL; /*空链表*//*申请空间,并将指针p强制转换成所指向的结构体类型*/p=(struct linklist*)malloc(sizeof(struct linklist));scanf("%d",&p−>data); /*得到数据,放结点数据域*/while(p−>data>0) /*假设所给数据都大于0*/{ p−>next=head;/*建立链接,当前结点指针域存放下一结点地址*/ head=p; /*用头指针保存当前结点*//*为下一个结点申请空间*/p= (struct linklist*)malloc(sizeof(struct linklist));scanf("%d",&p−>data); /*得到新结点数据*/ }}运行该程序,输入准备建立链表的各数据,以0结束数据的输入,即可得到一个链表。

由于是表首添加法,最后输出链表中的数据的输入顺序正好与输入顺序相反。

例2 表尾插入法建立链表若将链表的左端固定,链表不断向右延伸,这种建立链表的方法称为表尾插入法。

表尾插入法建立链表时,头指针固定不动,故必须设立一个搜索指针,向链表右边延伸,则整个算法中应设立三个链表指针,即头指针head、搜索指针p2、申请单元指针p1。

表尾插入法最先得到的是头结点。

#include <stdio.h>void main( ){struct linklist{ int data;struct linklist *next;};struct linklist *head,*p1,*p2;head=NULL; /*空链表*/p1=(struct linklist*)malloc(sizeof(struct linklist));/*申请空间*/p2=p1;scanf("%d",&p1->data); /*得到数据*/while(p1->data>0){ if(head==NULL) head=p1;else p2->next=p1; /*建立连接*/p2=p1; / *保存当前结点*//*为下一个结点申请空间*/p1=(struct linklist*)malloc(sizeof(struct linklist));scanf("%d",&p1−>data); /*得到下一个结点数据*/ }p2−>next=NULL;/*最后的尾结点指针域为空指针*/}运行该程序,输入准备建立链表的各数据,以0作为结束数据的输入,即可得到一个链表,该链表的输出顺序与输入顺序一致。

单链表的有关操作链表的基本操作包括建立链表、链表结点的插入、删除、输出和查找等。

下面介绍完成单链表的基本操作的函数。

1.输出链表中所有结点要依次输出链表中各结点的数据比较容易。

首先要知道表头结点的地址,即要知道head的值,然后设一个指针变量p,先指向第一个结点,输出p所指的结点,然后使p后移一个结点,再输出,直到链表的尾结点。

void print(struct linklist *head) /*输出链表所有结点*/{ struct linklist *p;p=head; /*p指向链表第一个结点*/while(p!=NULL){ printf("%d",p->data);p=p->next; /*p指向下一个结点*/}}head的值由实参传过来,即将已有的链表的头指针传给被调用的函数,在p rint函数中从head所指的第一个结点出发,顺序输出各结点,直到链表的结尾。

2.统计链表中结点个数只需将上述输出结点改成计数即可。

int count(struct linklist *head){ int n=0;struct linklist *p;p=head;while(p!=NULL){ n++;p=p->next;}return(n);}调用此函数,即可返回链表中结点的个数。

3.插入结点的操作将一个结点插入到一个已有链表的某个位置。

该任务可分解成两个步骤:即先查找到插入点,再插入结点。

指针head指向链表的头结点,p指针指向要插入结点的前一个结点,q指针指向新产生的结点。

void ins(struct linklist *head,int i,int x)/*在第i个结点处插入值为x结点*/{ int j;struct linklist *p,*q;p=head;j=1;while((p!=NULL)&&(j<i)) /*找插入位置*/{ p=p->next;j++;}q=(struct linklist *)malloc(sizeof(struct linklist));/*产生插入结点*/q->data=x;q->next=p->next; /*q插入p之后 */p->next=q;}运行该程序,根据所给的i值,在循环的控制下,找到第i个结点的前一个位置,用指针p指向它,然后实施在第i-1个(p指针所制结点之后)插入一个结点的操作。

4.删除操作假设删除链表中第i个结点,先找到第i-1个结点和第i个结点,然后将第i+1个结点链接在第i-1个结点后,即改变链接关系,再释放第i个结点所占空间,完成删除操作。

void del(struct linklist *head, int i) /*删除结点*/{ int j;struct linklist *p,*q;p=head;j=1;while((p!=NULL)&&(j<i))/*找第i-1个结点和第i个结点并用指针q、p指向*/{ q=p;p=p->next;j++;}if(p==NULL)printf("找不到结点!");else{ q->next=p->next; /*删除第i个结点 */free(p);}}5.链表的查找操作链表结点的查找是指在链表中查找值为某指定值的结点。

链表的查找过程是从链表的头指针所指的第一个结点出发,顺序查找;或发现有指定值的结点,以指向该结点的指针值为查找结果;或查找到链表的结尾,若未发现有指定值的结点,则查找结果为NULL,表示链表中没有指定值的结点。

在下例中,以指定学号作为查找结点的标志。

查找一个结点的函数find如下:struct student *find(struct student *head, int num){ struct student *p1,*p2;if(head==NULL){ printf("\n list null!\n");return(head);}p1=head;while(num!=p1->num&&p1!=NULL){ p2=p1;p1=p1->next;}if(p1!=NULL)printf("find:%d,%6.2f\n",p1->num, p1->score);else printf("%d not found!\n",num);}说明:一般查找操作总是与其它相关操作相联系的。

如:插入、删除操作等,故在查找的过程中,设置了两个工作指针p2与p1,p1指向要查找的结点,p2指向要查找结点的左边的一个结点。

以方便进行插入和删除操作。

例3 假设链表结点信息包括学生学号、成绩,结点定义如下:typedef struct plist{ int no;float score;struct plist *next;}plist;设已经建立了两个具有上述结构的链表,且两个链表都是以学号升序排列,要求编写一个函数,将两个链表(按升序)合并。

算法:①函数应有两个链表指针形参:p1, p2指向它们各自的表头;②最初链表的头指针head=NULL,新链表的当前结点指针p=NULL;③产生新链表的头结点;④当两个链表的指针均没指向表尾,则依次选择两个链表中的结点并入到新链表中;⑤当某一个链表已到表尾,则将另一个链表的剩余部分直接链接到新链表的表尾。

完成此功能的程序为:plist *merge(plist *p1, plist *p2)/*p1与p2分别为两个链表的头指针*/{ plist *p, *head;if(p1->no<p2->no ){ head=p=p1;p1=p1->next;} /*产生新链表的头结点*/else{ head=p=p2;p2=p2->next;}while(p1!=NULL &&p2!=NULL)if(p1->no<p2->no){ p->next=p1; /*p1指示的结点并入到新链表*/p=p1; /*p1指向新链表的表尾*/p1=p1->next; /*p1指向后继*/}else{ p->next=p2; /*p2指示的结点并入到新链表*/p=p2;p2=p2->next; /*p2指向后继*/}if(p1!=NULL) /*p1没有到达表尾*/p->next=p1; /*p1指示的链表后部分链接新链表的表尾*/ else p->next=p2; /*p2指示的链表后部分链接新链表的表尾*/return head;}循环while(p1!=NULL &&p2!=NULL)终止时,表示有p1指示的链表为空或p 2所指示的链表为空,再只将另一非空的链表(p2指示的链表或p1所指示的链表)链入到新链表中即可。