数据结构与算法操作实验二( 链表的操作)
一、实验目的
1.掌握线性表的链式存储结构的表示和实现方法。
2.掌握链表基本操作的算法实现。
二、实验内容
1.建立单链表,并在单链表上实现插入、删除和查找操作(验证性内容)。
2.建立双向链表,并在双向链表上实现插入、删除和查找操作(设计性内容)。
3.计算已知一个单链表中数据域值为一个指定值x的结点个数(应用性设计内容)。
三、实验的软硬件环境要求
硬件环境要求:
PC机(单机)
使用的软件名称、版本号以及模块:
Windows环境下的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<stdlib.h>
typedef struct node
{
int data;
struct node *next;
}Linklist;
Linklist *creat(int n)
{ int x;
Linklist *p,*q,*head=(Linklist*)malloc(sizeof(Linklist));
head->next=NULL;
q=head;
for(int i=1;i<=n;i++)
{
printf("输入数据");
scanf("%d",&x);
p=(Linklist*)malloc(sizeof(Linklist));
p->data =x;
p->next=NULL;
q->next=p;
q=p;
}
return head;
}
int print(Linklist *L)
{
Linklist *p=L;
while(p->next!=NULL)
{
p=p->next ;
printf("%4d",p->data );
}
printf("\n");
}
insert(Linklist *L,int i,int e)
{ int j=1;
Linklist *q,*p=L->next;
while(p&&j<i-1)
{
j++;
p=p->next;
}
q=(Linklist*)malloc(sizeof(Linklist));
q->data =e;
q->next =p->next ;
p->next =q;
}
delet (Linklist *L,int i)
{
int j=1;
Linklist *q,*p=L->next;
while(p&&j<i-1)
{
j++;
p=p->next ;
}
q=p->next ;p->next =q->next ;
free(q);
}
search (Linklist *L,int key)
{
Linklist *p=L->next;
while(p&&p->data !=key)
{
p=p->next ;
if(p->data==key)
{
printf("OK");break;
}
else
{
printf("NOT FOUND");break;
}
}
}
int main()
{
int n,i,e,key;
printf("输入表的长度");
scanf("%d",&n);
Linklist *head=creat(n);
print(head);
printf("请输入要插入的位置:");
scanf("%d",&i);
printf("请输入要插入的值e:");
scanf("%d",&e);
insert(head,i,e);
print(head);
printf("请输入要删除的位置i:");
scanf("%d",&i);
delet(head,i);
print(head);
printf("请输入要查找key:");
scanf("%d",&key);
search(head,key);
print(head);
}
5. 运行结果

六、设计性实验(以下两个设计题目学生可根据自己的掌握程度或兴趣自行选择完成)
- 编程实现在双向循环链表上的插入、删除和查找操作
⑴ 实验要求
(1)输入链表的长度和各元素的值,用尾插法建立双向循环链表,并输出链表中各元素值,观察输入的内容与输出的内容是否一致。
(2)在双向循环链表的第i个元素之前插入一个值为x的元素,并输出插入后的链表中各元素值。
(3)删除双向循环链表中第i个元素,并输出删除后的链表中各元素值。
(4)在双向循环链表中查找值为x元素,如果查找成功,则显示该元素在链表中的位置,否则显示该元素不存在。
⑵ 核心算法提示
双向循环链表采用的存储结构描述如下:
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;
}
提醒: 请将上述算法与单链表上相应操作的算法对照学习,特别注意它们不相同的地方。
2.编写一个程序,计算出一个单链表中数据域值为一个指定值x的结点个数。
实验要求:
⑴ 从键盘输入若干个整数,以此序列为顺序建立一个不带头结点的单链表;
⑵ 输出此单链表中的各个数据元素值;
⑵ 给定一个x的具体整数值,计算并返回此单链表中数据域值为x的结点个数值。
本实验旨在掌握线性表的链式存储结构,包括单链表和双向循环链表的建立、插入、删除和查找操作。学生需要使用C语言编程实现这些功能,同时计算单链表中特定值出现的次数。实验要求涉及头插法、尾插法、数据域值计数等核心算法。
2426





