scanf("\n%d",&L->elem[i]);
returnOK;
}
//以下是整个源程序(保存在shiyan2_1_1.c文件中):
#include
#include"SqList.h"
voidmain()
{
inti,n;
SqLista;
SqList*l=&a;
if(InitList_Sq(l)=-2)printf("分配失败\n");
printf("\n输入要建立的线性表l的长度n:
");//输入线性表的长度
scanf("%d",&n);
l->length=n;
printf("线性表的长度是:
%d\n",l->length);
CreatList_Sq(l,n);//生成线性表
printf("输出线性表l中的元素值:
");//输出线性表中的元素
for(i=0;ilength;i++)
printf("%7d",l->elem[i]);
getch();
}
[实验2]顺序表的插入
实验内容与要求:
利用前面的实验先建立一个顺序表L={21,23,14,5,56,17,31},然后在第i个位置插入元素68,通过对比插入元素前后的线性表发生的变化,判断插入操作是否正确。
分析:
从插入位置到最后位置的所有元素都要后移一位,使空出的位置插入元素值e。
一般在第i个元素之前(1<=i<=n)插入一个新元素时,需要有n-i+1个元素进行移动。
但是,插入的位置是不固定的,当插入位置i=1时,全部元素都得移动,需n次移动,当i=2时,移动n-1个元素,依次类似,当i=n时,仅需移动元素一次。
如图2-2所示。
注意如何取到第i个元素,在插入过程中注意溢出情况以及数组的下标与位序(顺序表中元素的次序)的区别。
参考程序:
//保存在shiyan2_1_2.c文件中
#include
#include
#include"SqList.h"
StatusListInsert_Sq(SqList*L,inti,ElemTypee){
//在线性表L中的第i个位置前插入一个值为e的元素
//i的取值范围:
1<=i<=ListLength_Sq(L)
ElemType*newbase,*q,*p;
if(i<1||i>L->length+1)returnERROR;//i值不合法
if(L->length>=L->listsize){//当前存储空间已满,增加分配量
newbase=(ElemType*)realloc(L->elem,
(L->listsize+LISTINCREMENT)*sizeof(ElemType));
if(!
newbase)return(OVERFLOW);//存储分配失败
L->elem=newbase;//新基址
L->length=+LISTINCREMENT;//增加存储容量
}//if
q=&(L->elem[i-1]);//q为插入位置
for(p=&(L->elem[L->length-1]);p>=q;--p)*(p+1)=*p;
//插入位置及以后的元素右移
*q=e;//插入e
++L->length;//表长增1
returnOK;
}//ListInsert_Sq
voidmain()
{
intn,i,x;
SqList*L,a;
L=&a;
InitList_Sq(L);
printf("\n输入要建立的线性表L的长度:
");
scanf("%d",&n);
L->length=n;
CreatList_Sq(L,n);
printf("\n插入元素之前线性表L的长度是:
%d",L->length);
printf("\n插入元素之前线性表L中的元素是:
");
for(i=0;ilength;i++)
printf("%5d",L->elem[i]);
printf("\n输入要插入元素的位置:
");
scanf("%d",&i);
printf("\n输入要插入的元素的值:
");
scanf("\n%d",&x);
if(ListInsert_Sq(L,i,x)>0)
{
printf("\n插入元素之后线性表L的长度是:
%d",L->length);
printf("\n插入元素之后线性表L的元素是:
\n");
for(i=0;ilength;i++)
printf("%5d",L->elem[i]);
}//if
else
printf("can'tinsertthedata!
\n");
getch();
}
[实验3]单链表的建立
实验内容与要求:
建立一个带头结点的单链表,结点的值域为整型数据。
要求将用户输入的数据按尾插入法来建立相应单链表。
分析:
为了便于实现各种运算,通常在单链表的第一个结点前增设一个附加结点,称为头结点,它的结构与表结点相同,其数据域可不存储信息,也可存储表长等附加信息,具体见图2_3。
(a)不带头结点
(b)带头结点的单链表
单链表的结点结构除数据域外,还含有一个指针域。
用C语言描述结点结构如下:
typedefintElemType;
typedefstructLNode
{
ElemTypedata;//数据域
structLNode*next;//指针域
}Lnode,*Linklist;
注意结点的建立方法及构造新结点时指针的变化。
构造一个结点需用到C语言的标准函数malloc(),如给指针变量p分配一个结点的地址:
p=(Linklist)malloc(sizeof(LNode));该语句的功能是申请分配一个LNode类型结点的地址空间,并将首地址存入指针变量p中。
当结点不需要时可以用标准函数free(p)释放结点存储空间,这时p为空值(NULL)。
参考程序:
//保存在头文件LinkList.h
#include
#include
#defineNULL0
typedefintElemType;
typedefstructLNode{
ElemTypedata;
structLNode*next;
}LNode,*LinkList;
voidCreatList_L(LinkListL,intn)
{
inti;
LinkListp,q;
q=L;
for(i=1;i<=n;i++)
{
p=(LinkList)malloc(sizeof(LNode));
printf("输入线性表的第%d个元素:
",i);
scanf("%d",&p->data);
p->next=q->next;
q->next=p;
q=q->next;
}
}
//以下程序保存在文件shiyan2_1_3.c中:
#include"LinkList.h"
voidmain()
{
LinkListhead,p;
intn;
printf("\n输入要建立链表的长度:
");
scanf("%d",&n);
head=(LinkList)malloc(sizeof(LNode));
head->next=NULL;
CreatList_L(head,n);
printf("\n以head为头指针的链表中的元素:
");
p=head->next;
while(p)
{
printf("%5d",p->data);
p=p->next;
}
getch();
}
[实验4]单链表的合并
实验内容与要求:
建立两个带头结点的有序单链表La,Lb(单调递增),利用La,Lb的结点空间,将La和Lb合并成一个按元素值递增的有序单链表Lc。
分析:
对于单链表的操作必须清楚如何将结点插入到链表中,比如将新结点x插入到结点a和b之间,插入结点的指针变化如图2-4所示。
程序需要3个指针:
pa,pb,pc,其中pa,pb分别指向La表、Lb表的首结点,用pa遍历La表,pb遍历Lb表,pc指向合并后的新表的最后一个结点(即尾结点),pc的初值指向La表的头结点。
合并的思想是:
先利用实验3的内容建立好两个链表La表和Lb表,然后依次扫描La和Lb中的元素,比较当前元素的值,将较小者链到*pc之后,如此重复直到La或Lb结束为止,再将另一个链表余下的内容链到pc所指的结点之后。
参考程序:
//以下是程序保存在shiyan2_1_4.c文件中
#include"LinkList.h"
voidMergeList_L(LinkListLa,LinkListLb,LinkListLc)
{
LinkListpa,pb,pc;
pa=La->next;
pb=Lb->next;
pc=Lc;
while(pa&&pb)
{
if(pa->data>pb->data)
{
pc->next=pb;
pc=pb;
pb=pb->next;
}
else
{
pc->next=pa;
pc=pa;
pa=pa->next;
}
}
pc->next=pa?
pa:
pb;
free(Lb);
}
voidmain()
{
LinkListLa,Lb,Lc,p;
intn1,n2;
La=(LinkList)malloc(sizeof(LNode));
La->next=NULL;
Lb=(LinkList)malloc(sizeof(LNode));
Lb->next=NULL;
Lc=NULL;
printf("\n请输入要建立的链表La的长度:
");
scanf("%d",&n1);
CreatList_L(La,n1);
printf("\n请输入要建立的链表Lb的长度");
scanf("%d",&n2);
CreatList_L(Lb,n2);
p=La->next;
printf("\n链表La中的元素:
");
while(p)
{
printf("%5d",p->data);
p=p->next;
}
p=Lb->next;
printf("\n链表Lb中的元素:
");
while(p)
{
printf("%5d",p->data);
p=p->next;
}
MergeList_L(La,Lb,Lc);
p=Lc->next;
printf("\n输出由链表La和Lb归并得到的链表Lc中的元素:
\n");
while(p)
{
printf("%d",p->data);
p=p->next;
}
getch();
}
[实验5]删除单链表中的重复值
实验内容与要求:
编写算法实现将单链表L中值重复的结点删除,使所得结果表中各结点值均不相同。
分析:
本题涉及到单链表中关于值的查找算法,从头结点开始,活动指针p沿着单链表向后扫描,寻找某个给定值。
首先建立一个单链表,令p指针指向所建单链表的第一个结点,令q指向p的后继结点,q沿着链表向右(向后)扫描,若找到与p所指结点值相同的结点,则将其删除,继续处理,直到q为空;然后令p移到下一个结点(即直接后继结点),q依然指向p的后继结点,重复同样的处理。
显然此题需要用到双重循环。
参考程序:
//以下程序保存在shiyan2_1_5.c
#include"LinkList.h"
//DeleteSameNode函数用来删除链表中重复的结点
voidDeleteSameNode(LinkListh)
{
LinkListp,q,s
p=h->next;
s=p;
while(p!
=NULL)
{
q=p->next;
while(q!
=NULL)
{
if(q->data!
=p->data)
{
s=q;//s为q的直接前趋指针,即s紧跟着q向右移动。
q=q->next;
}//if
else
{
s->next=q->next;//此时q所指向的结点为待删除结点
free(q);
q=s->next;//q指向后继结点,继续寻找与p所指结点值相同的结点。
}//else
}//while(q!
=NULL)
p=p->next;
s=p;
}//while(p!
=NULL)
}
//OutPut函数用来输出单链表的内容
voidOutPut(LinkListh)
{
LinkListp;
p=h->next;
while(p)
{
printf("%5",p->data);
p=p->next;
}//while
}
voidmain()
{
LinkListhead;
intn;
head=(LinkList)malloc(sizeof(LNode));
head->next=NULL;
printf("请输入要建立的链表的长度:
\n");
scanf("%d",&n);
CreatList(head,n);
printf("链表中的元素是:
\n");
output(head);
printf("删除链表中结点的重复值!
\n");
DeleteSameNode(head);
OutPut(head);//输出经过处理后的单链表,此时单链表中的值应该唯一。
}
[实验6]单循环链表的逆置
实验内容与要求:
将一个已知的单循环链表进行逆置运算,如(a1,a2,a3,…,an)变为(an,an-1,…,a2,a1)。
分析:
对于单链表而言,最后一个结点的指针域是空指针,如果将该链表头指针置入该指针域,则使得链表头尾结点相连,就构成了单循环链表。
对于单链表只能从头结点开始遍历整个链表,而对于单循环链表则可以从表中任意结点开始遍历整个链表。
所谓链表的逆置运算(或称为逆转运算)是指在不增加新结点的前提下,依次改变数据元素的逻辑关系,使得线性表(a1,a2,a3,…,an)成为(an,an-1,…,a2,a1)。
本题采用的算法是:
先建立一个带头结点的单循环链表,从头到尾扫描单链表L,把p作为活动指针,沿着链表向前移动,q作为p前趋结点,r作为q的前趋结点。
其中,q的next值为r,r的初值置为head。
参考程序:
//头文件LNode.h的内容:
#include
#include
#defineNULL0
typedefintElemType;
typedefstructLNode
{
ElemTypedata;//数据域
structLNode*next;//指针域
}LNode,*LinkList;
//以下是主程序保存在shiyan2_1_6.c中
#include“LNode.h”
//输出循环链表的信息
voidOutPut(LinkListhead)
{
LinkListp;
p=head->next;
while(p!
=head)
{
printf("%5d",p->data);
p=p->next;
}//while
}
//建立单循环链表
voidCreat(LinkListh,intn)
{
intk;
LNode*r,*p;
r=h;
for(k=1;k<=n;k++)
{
printf(“输入第%d个结点的数值:
”,k);
p=(LNode*)malloc(sizeof(LNode));
scanf(“%d”,&p->data);
p->next=h;
r->next=p;
r=p;
}
}
//逆置函数
voidInvert(LinkListh)
{
LinkListp,q,r;
p=h->next;
q=h;
while(p!
=h)
{
r=q;
q=p;
p=p->next;
q->next=r;
}
h->next=q;
}
voidmain()
{
intn;
LinkListhead;
printf("\n输入所建立的循环链表的结点个数:
");
scanf("%d",&n);
head=(LNode*)malloc(sizeof(LNode));
head->next=head;
Creat(head,n);
printf("输出建立的单循环链表:
");
OutPut(head);
printf("\n现在进行逆置!
\n");
Invert(head);
printf("\n输出进行逆置运算后的单循环链表的结点信息:
");
OutPut(head);
}
2.4提高实验
[实验1]学生成绩管理
实验内容与要求:
学生成绩管理是学校教务管理的重要组成部分,其处理信息量很大,本实验是对学生的成绩管理作一个简单的模拟,用菜单选择操作方式完成下列功能:
①登录学生成绩;
②查询学生成绩;
③插入学生成绩;
④删除学生成绩
分析:
本实验涉及单链表的各种操作,包括单链表的建立、结点的查找、插入、删除等基本运算。
链表中插入结点的指针变化见图2-4所示,删除p所指结点的指针变化见图2-5所示。
首先建立学生成绩单链表,每条记录由学号、姓名与成绩组成,即链表中每个结点由4个域组成,分别为:
学号、姓名、成绩、存放下一个结点地址的next域。
然后将要求完成的四项功能写成四个函数,登记学生成绩对应建立学生单链表的功能,后三个功能分别对应单链表的查询、插入与删除三大基本操作。
该系统中的数据采用线性表中的链式存储结构即单链表来存储,用结构体类型定义每个学生记录,这样单链表中每个结点的结构可描述为:
#defineMAXLEN100
typedefstructNode
{
intnum;//学号
charname[MAXLEN];//姓名
floatscore;//成绩
structNode*next;
}Node;
参考程序
//头文件hh.h的内容
#include
#include
#include
#defineMAXLEN100
#defineNULL0
typedefstructNode
{
intnum;
charn