链表的操作实验
一、实验名称和性质
二、实验目的
1.掌握线性表的链式存储结构的表示和实现方法。
2.掌握链表基本操作的算法实现。
三、实验内容
1.建立单链表,并在单链表上实现插入、删除和查找操作(验证性内容)。
2.建立双向链表,并在双向链表上实现插入、删除和查找操作(设计性内容)。
3.计算已知一个单链表中数据域值为一个指定值x的结点个数(应用性设计内容)。
四、实验的软硬件环境要求
硬件环境要求:PC机(单机)
使用的软件名称、版本号以及模块:
Windows环境下的TurboC2.0以上或VC++ 等。
五、知识准备
前期要求熟练掌握了C语言的编程规则、方法和单链表和双向链表的基本操作算法。
六、验证性实验
1.实验要求
编程实现如下功能
(1)根据输入的一系列整数,以0标志结束,用头插法建立单链表,并输出单链表中各元素值,观察输入的内容与输出的内容是否一致。
(2)在单链表的第i个元素之前插入一个值为x的元素,并输出插入后的单链表中各元素值。
(3)删除单链表中第i个元素,并输出删除后的单链表中各元素值。
(4)在单链表中查找第i个元素,如果查找成功,则显示该元素的值,否则显示该元素不存在。
2. 实验相关原理:
线性表的链式储结构是用一组任意的存储单元依次存放线性表中的元素,这组存储单元 可以是连续的,也可以是不连续的。为反映出各元素在线性表中的前后逻辑关系,对每个 数据元素来说,除了存储其本身数据值之外,还需增加一个或多个指针域,每个指针域的值称为指针,又称作链,它用来指示它在线性表中的前驱或后继的存储地址。这两个部分的的一起组成一个数据元素的存储映像,称为结点,若干个结点链接成链表。如果一个结点中只含一个指针的链表,则称单链表。单链表的存储结构描述如下:
typedef struct Lnode {
Elemtype data;/*数据域*/
struct Lnode *next;/*指针域*/
}LNODE,*Linklist; /*其中LNODE为结点类型名,Linklist为指向结点的指针类型名*/
【核心算法提示】
1.链表建立操作的基本步骤:链表是一个动态的结构,它不需要予分配空间,因此建立链表的过程是一个结点“逐个插入” 的过程。先建立一个只含头结点的空单链表,然后依次生成新结点,再不断地将其插入到链表的头部或尾部,分别称其为“头插法”和“尾插法”。
2.链表查找操作的基本步骤:因链表是一种"顺序存取"的结构,则要在带头结点的链表中查找到第 i个 元素,必须从头结点开始沿着后继指针依次"点数",直到点到第 i 个结点为止,如果查找成功,则用e返回第i个元素值。头结点可看成是第0个结点。
3.链表插入操作的基本步骤:先确定要插入的位置,如果插入位置合法,则再生成新的结点,最后通过修改链将新结点插入到指定的位置上。
4.链表删除操作的基本步骤:先确定要删除的结点位置,如果位置合法,则再通过修改链使被删结点从链表中“卸下”,最后释放被删结点的空间。
【核心算法描述】
void creat1(Linklist &L) /*输入一系列整数,以0标志结束,将这些整数作为data域并用头插法建立一个带头结点的单链表的函数*/
{
L=(Linklist)malloc(sizeof(LNODE));/*生成头结点*/
L->next=NULL;/*头结点的指针域初始为空*/
scanf("%d",&node);
while(node!=0)
{
p=(Linklist)malloc(sizeof(LNODE));/*为一个新结点的分配空间*/
p->data=node; /*为新结点数据域赋值*/
p->next=L->next;/*新结点指针域指向开始结点*/
L->next=p; /*头结点指针域指向新结点,即新结点成为开始结点*/
scanf("%d",&node);
}
}
void creat2(Linklist &L) /*输入一系列整数,以0标志结束,将这些整数作为data域并用尾插法建立一个带头结点的单链表的函数*/
{
L=(Linklist)malloc(sizeof(LNODE));/*为头结点分配空间*/
L->next=NULL; /*头结点的指针域初始为空*/
r=L; /*尾指针初始指向头结点*/
scanf("%d",&node);
while(node!=0)
{
p=(Linklist)malloc(sizeof(LNODE));/*为一个新结点分配空间*/
p->data=node; /*新结点数据域赋值*/
p->next=NULL; /*新结点指针域为空*/
r->next=p;/*尾结点指针域指向新结点*/
r=p; /*尾指针指向新结点,即新结点成为尾结点*/
scanf("%d",&node);
}
}
status list_search(Linklist L, int i;Elemtype &e)
/*在带头结点的单链表L中查找第i个元素,如果查找成功则用e返回其值*/
{
p=L->next; /*使指针p指向第1个元素结点*/
j=1; /*计数器赋初值为1*/
while (p&& j<i) /*顺着后继指针查找第i个元素结点*/
{
p=p->next;
j++;
}
if (!p&& j>i)
return ERROR; /*如果i值不合法,即i值小于1或大于表长,则出错*/
e=p->data; /*如果第i个元素存在,则将该元素值赋给e*/
return OK;
}
status list_insert(Linklist &L,int i;Elemtype x)
/*在带头结点的单链表L中第i个位置之前插入新元素x*/
{
p=L; j=0;
while(p!=NULL&&j<i-1) /*寻找插入位置,并使p指向插入位置的前驱结点,即L中的第i-1个位置*/
{
p=p->next;
++j;
}
if(p==NULL||j>i-1)
return ERROR; /*若位置不正确,即i小于1或大于表的长度加1,则出错*/
s=(Linklist)malloc(sizeof(LNODE)); /*分配一个新结点的空间*/
s->data=x; /*为新结点数据域赋值*/
/*下面两条语句就是完成修改链,将新结点s插入到p结点之后*/
s->next=p->next; /*新结点指针域指向p的后继结点*/
p->next=s; /*新结点成为p的后继结点*/
return OK;
}
status list_delete(Linklist &L,int i,Elemtype &x)
/*在带头结点的单链表L中,删除第i个元素结点,并用x返回其值*/
{
p=L;j=0;
while(p->next!=NULL&&j<i-1) /*寻找被删结点,并使p指向被删结点的前驱*/
{
p=p->next;
++j;
}
if (p->next==NULL||j>i-1)
return ERROR; /*若位置不正确,即i小于1或大于表长,则出错*/
q=p->next; /* q指向p的后继结点*/
p->next=q->next; /*q的后继结点成为p的后继结点*/
x=q->data; /*用x返回第i个位置的元素*/
free(q); /*释放q所指的被删结点的空间*/
return OK;
}
3.源程序代码参考
#include <stdio.h>
#include <malloc.h>
typedef struct Lnode
{
int data;
struct Lnode *next;
} LNODE,*Linklist;
Linklist creat(Linklist L) /*单链表建立函数 */
{
int node; Linklist p;
L=(Linklist)malloc(sizeof(LNODE));
L->next=NULL;
printf("\nplease input the node(end with 0):\n ");
/*请求输入线性表中各个元素,以0结束*/
scanf("%d",&node);
while(node!=0)
{
p=(Linklist)malloc(sizeof(LNODE));
if (!p) exit();
p->data=node;
p->next=L->next;
L->next=p;
scanf("%d",&node);
}
return L;
}
Linklist insert(Linklist L,int i,int x) /*单链表插入函数*/
{
int j;Linklist p,s;
p=L; j=0;
while (p!=NULL&&j<i-1)
{
p=p->next;
++j;
}
if (p==NULL||j>i-1)
printf("\n ERROR position!\n");
else
{
s=(Linklist)malloc(sizeof(LNODE));
s->data=x;
s->next=p->next;
p->next=s;
}
return L;
}
Linklist delete(Linklist L,int i) /*单链表删除函数*/
{
int j,x; Linklist p,q;
p=L; j=0;
while(p->next!=NULL&&j<i-1)
{
p=p->next;
++j;
}
if(p->next==NULL||j>i-1)
printf("\n ERROE position!\n");
else
{
q=p->next;
p->next=q->next;
x=q->data;
printf("the delete data is:%d\n",x);
free(q);
}
return L;
}
int search(Linklist L, int i) /*单链上的查找函数*/
{
Linklist p; int j;
p=L->next;
j=1;
while (p&& j<i)
{
p=p->next;
j++;
}
if (!p&& j>i)
return 0; /*如果i值不合法,即i值小于1或大于表长,则函数返回0值*/
else
return(p->data); /*否则函数返回第i个元素的值*/
}
void display(Linklist L) /*单链表元素输出函数*/
{
Linklist p;
p=L->next;
while(p!=NULL)
{
printf("%4d",p->data);
p=p->next;
}
printf("\n");
}
main()/*主函数*/
{
Linklist L=NULL; int i,x;
L=creat(L);/*调用单链表建立函数*/
printf("the Linklist is:\n");
display(L); /*调用单链表元素输出(遍历)函数*/
printf("please input the position you want to insert:");/*请求输入插入操作位置*/
scanf("%d",&i);
printf("please input the node you want to insert:");/*请求输入需要插入的元素*/
scanf("%d",&x);
L=insert(L,i,x);/*调用单链表插入函数*/
printf("the Linklist after inserted is:\n");
display(L);/*调用单链表元素输出(遍历)函数*/
printf("please input the node position you want to delete:"); /*请求输入删除操作位置*/
scanf("%d",&i);
L=delete(L,i); /*调用单链表删除函数*/
printf("the Linklist after deleted is:\n");
display(L); /*调用单链表元素输出(遍历)函数*/
printf("please input the position you want to search:"); /*请求输入待查找元素的位置*/
scanf("%d",&i);
x=search(L,i); /*调用单链表查找函数*/
if (x) /*如果查找成功,则显示第i个元素的值,否则显示第i个元素不存在*/
printf(" the %dth elem is %d\n",i,x);
else
printf(" the %dth elem is not exsit\n");
}
4.运行结果参考如图2-1所示:
七、设计性实验
1.实验要求
(1)输入链表的长度和各元素的值,用尾插法建立双向循环链表,并输出链表中各元素值,观察输入的内容与输出的内容是否一致。
(2)在双向循环链表的第i个元素之前插入一个值为x的元素,并输出插入后的链表中各元素值。
(3)删除双向循环链表中第i个元素,并输出删除后的链表中各元素值。
(4)在双向循环链表中查找值为x元素,如果查找成功,则显示该元素在链表中的位置,否则显示该元素不存在。
2, 核心算法提示
双向循环链表采用的存储结构描述如下:
typedef struct DULNODE
{
Elemtype data; /*数据域*/
struct DULNODE *prior; /*指向前驱结点的指针域*/
struct DULNODE *next;/*指向后继结点的指针域*/
}DULNODE,*DuLinklist;
typedef int Elemtype;
不论是建立双向循环链表还是在双向循环链表中进行插入、删除和查找操作,其操作方法和步骤都跟单链表类似。只不过要注意两点:
(1)凡是在操作中遇到有修改链的地方,都要进行前驱和后继两个指针的修改。
(2)单链表操作算法中的判断条件:p= =NULL 或p!=NULL ,在循环链表的操作算法中则需改为:p!= L,其中L为链表的头指针。
⑶ 核心算法描述
void DuList_creat (DuLinklist &L,int n) /*输入n个整数(其中n为表长),将这些整数作为data域并用尾插法建立一个带头结点的双向循环链表的函数*/
{
L=( DuLinklist)malloc(sizeof(DULNODE));/*为头结点分配空间*/
L->next=L->prior=L;
/*使头结点的后继指针和前驱指针都指向自身,形成空的双向循环链表*/
r=L; /*尾指针初始指向头结点*/
for (i=0;i<n;i++)
{
p=(DuLinklist)malloc(sizeof(DULNODE));/*为一个新结点分配空间*/
scanf("%d",&p->data); /*从键盘输入值,并保存在新结点数据域中*/
p->next=r->next; /*新结点后继指针指向尾结点r的后继结点*/
p->prior=r; /*新结点的前驱指针指向尾结点r*/
r->next=p; /*尾结点的后继指针指向新结点*/
r=p; /*尾指针指向新结点,即新结点成为尾结点*/
}
L->prior=r; /*使尾结点成为头结点的前驱结点*/
}
status DuList_search(DuLinklist L, int i;Elemtype &e)
/*在带头结点的双向循环链表L中查找第i个元素,如果查找成功则用e返回其值*/
{
p=L->next; /*使指针p指向第1个元素结点*/
j=1; /*计数器赋初值为1*/
while (p!=L && j<i) /*顺着后继指针查找第i个元素结点*/
{
p=p->next;
j++;
}
if (j!=i)
return ERROR; /*如果i值不合法,即i值小于1或大于表长,则出错*/
e=p->data; /*如果第i个元素存在,则将该元素值赋给e*/
return OK;
}
status DuList_insert(DuLinklist &L,int i;Elemtype x)
/*在带头结点的双向循环链表L中第i个位置之前插入新元素x*/
{
p=L->next; j=1;
while(p!=L &&j<i) /*寻找插入位置,并使p指向插入位置的结点,即L中的第i个结点*/
{
p=p->next;
++j;
}
if(j!=i)
return ERROR; /*若位置不正确,即i小于1或大于表的长度加1,则出错*/
s=(DuLinklist)malloc(sizeof(DULNODE)); /*为一个新结点s分配空间*/
s->data=x; /*为新结点数据域赋值*/
/*下面四条语句就是完成修改链,将新结点s插入到p结点之前*/
s->next=p;
p->prior->next=s;
s->prior=p->prior;
p->prior=s;
return OK;
}
status DuList_delete(DuLinklist &L,int i,Elemtype &x)
/*在带头结点的双向循环链表L中,删除第i个元素结点,并用x返回其值*/
{
p=L->next;j=1;
while(p!=L&&j<i) /*寻找被删结点,并使p指向被删结点的前驱*/
{
p=p->next;
++j;
}
if (j!=i)
return ERROR; /*若位置不正确,即i小于1或大于表长,则出错*/
q=p; /*记下被删结点*/
p->prior->next=p->next; /*修改链使得p结点从链中脱离*/
p->next->prior=p->prior;
x=q->data;
printf("the delete data is:%d\n",x);
free(q); //释放被删结点空间
return OK;
}
提醒: 请将上述算法与单链表上相应操作的算法对照学习,特别注意它们不相同的地方。