数据结构实验手册修订版.docx

上传人:b****6 文档编号:6028110 上传时间:2023-01-03 格式:DOCX 页数:31 大小:50.63KB
下载 相关 举报
数据结构实验手册修订版.docx_第1页
第1页 / 共31页
数据结构实验手册修订版.docx_第2页
第2页 / 共31页
数据结构实验手册修订版.docx_第3页
第3页 / 共31页
数据结构实验手册修订版.docx_第4页
第4页 / 共31页
数据结构实验手册修订版.docx_第5页
第5页 / 共31页
点击查看更多>>
下载资源
资源描述

数据结构实验手册修订版.docx

《数据结构实验手册修订版.docx》由会员分享,可在线阅读,更多相关《数据结构实验手册修订版.docx(31页珍藏版)》请在冰豆网上搜索。

数据结构实验手册修订版.docx

数据结构实验手册修订版

第一部分C语言基本知识

《数据结构》是计算机专业及相关专业的核心基础课。

上机实验是对学生的一种全面综合训练,是与课堂听讲、自学和练习相辅相成的必不可少的一个教学环节。

通常,实验题中的问题比平时的习题复杂得多,也更接近实际。

实验着眼于原理与应用的结合点,使学生学会如何把书上学到的知识用于解决实际问题,培养软件工作所需要的动手能力;另一方面,能使书上的知识变“活”,起到深化理解和灵活掌握教学内容的目的。

目前各种“数据结构”教材较为注重理论的叙述与介绍,算法描述不拘泥某种语言的语法细节。

多年的教学实践表明,学生的程序设计基础并不一致,相当一部分人基础较为薄弱,对上机实验感到非常困难。

存在的主要问题是:

不能正确的输入数据,结构体概念陌生,函数的传址调用概念不清,有关指针的内容理解不深。

因此,有必要将数据结构所必须使用的C语言语法在此做简单介绍。

如果学生基础好,可以跳过这一部分内容不看。

一、基本输入和输出

看起来简单的输入/输出,往往是上机实验最容易出错的地方,尤其是输入。

对于一个算法程序,如果数据不能正确输入,算法设计得再好也无法正常运行。

1.输入

C语言的输入是由系统提供的scanf()等函数实现,在程序的首部一般要求写入:

#include

因为标准输入/输出函数都存在于头文件stdio.h之中,现将其包含进来方可使用这些常用的输入/输出函数。

有的系统允许不使用上述包含语句,可以直接使用标准输入/输出函数。

函数scanf()的功能很丰富,输入格式也是多种多样,这是大家较为熟悉的知识,这里不做详细介绍。

在使用中需要注意以下几个问题。

(1)一条scanf()语句有多个变量、并且都是数值型(int,float,double)时,在输入数据时应该在一行之内键入多个数据,数据之间空格分隔。

例如:

intn;floatx;

scanf(“%d%f”,&n,&x);

正确的输入应是:

整数空格实数回车。

例如:

就是在两个数据之间使用空格键为分隔符,最后打回车键。

如果语句中在%d和%f之间有一个逗号:

scanf(“%d,%f”,&n,&x);

正确的输入应是:

整数逗号实数回车。

例如:

(2)在需要字符型变量或字符串输入时,要单独写一条输入语句,这样不易出错。

如果在同一条scanf()语句中将字符型和数值型混合输入常常会出错。

因为键盘输入时在数值型数据之间‘空格键’起‘分隔符’作用,但是在字符或字符串之间,‘空格’会被当做一个字符,而不能起到‘分隔符’的作用。

所以将它们混在一起容易出错。

(3)在scanf()语句中变量写法应该是该变量的地址,这一点常被忽视。

例1请看下列程序:

1:

viodmain()

2:

{charname[10],ch;

3:

intnum;floatx;

4:

printf(“\n请输入姓名:

”);scanf(“%s”,name);

5:

printf(“\n请输入性别:

”);scanf(“%c”,&ch);

6:

printf(“\n请输入学号和成绩:

”);scanf(“%d%f”,&n,&x);

……;

}

为了方便说明问题程序中加了行号,运行时当然不允许行号。

一般情况下在scanf()语句中的变量名之前要加上求地址符&,上述程序第5,6行之中就是这样。

为什么第4行的name前面不加&呢?

因为name代表字符串,即是一维字符数组,一维数组名本身就是一个地址,是该数组的首地址,所以name前面不加&。

在本程序中把字符串、字符、数值型变量分别写入不同的scanf()语句,输入数据的具体形式如下:

请输入姓名:

ZhangHua

请输入性别:

v

请输入学号和成绩:

10190.5

请考虑如果姓名输入成:

ZhangHua,会出现什么现象?

那样只会读入Zhang做姓名,而Hua被忽略,还会影响后面的输入语句无法正确读入数据。

因此,应该充分重视数据的输入技术。

2.输出

C语言的输出是由系统提供的printf()等函数来实现,在程序的首部一般要求写入:

#include

因为标准输入/输出函数都存在于头文件stdio.h之中,现将其包含进来方可使用这些常用的输入/输出函数。

有的系统允许不使用上述包含语句,可以直接使用标准输入/输出函数。

输出函数printf()的语法一般容易掌握,这里强调的是怎样合理巧妙的使用它。

(1)在连续输出多个数据时,数据之间一定要有间隔,不能连在一起。

intn=10,m=20,p=30;

printf(“\n%d%d%d”,n,m,p);

printf(“\n%6d%6d%6d”,n,m,p);//提倡使用的语句

第一行输出是:

102030

第二行输出是:

102030

(2)在输入语句scanf()之前先使用printf()输出提示信息,但是在printf()最后不能使用换行符。

intx;

printf(“\nx=?

”);//句尾不应使用换行符

scanf(“%d”,&x);

这样使光标与提示信息出现在同一行上,光标停在问号后边:

X=?

□。

(3)在该换行的地方,要及时换行。

inti;

printf(“数据输出如下:

\n”);//需要换行

for(i=0;i<8;i++)printf(“%6d”,i);//几个数据在同一行输出,不能换行

(4)在调试程序时多加几个输出语句,以便监视中间运行状况。

程序调式成功后,再去掉这些辅助输出语句。

二、函数与参数传递

C语言的源程序是由一个主函数和若干(或零个)子函数构成,函数是组成C语言程序的基本单位。

函数具有相对独立的功能,可以被其他函数调用,也可调用其他函数。

当函数直接或间接的调用自身时,这样的函数称为递归函数。

是否能够熟练的设计和使用函数,是体现一个人程序设计能力高低的基本条件。

因此有必要回顾和复习C语言函数的基本概念。

1函数的设计

函数设计的一般格式是:

类型名函数名(形参表)

{函数体;}

函数设计一般是处理一些数据获得某个结果,因此函数可以具有返回值,上面的类型名就是函数返回值的类型,可以是int,float…..等。

例如:

floatfunx(形参表){函数体;.}

函数也可无返回值,此时类型是void。

例如:

voidfuny(形参表){函数体;}

而函数体内所需处理的数据往往通过形参表传送,函数也可以不设形参表,此时写为:

类型名函数名(){函数体;}

例2设计一个函数计算三个整数之和,再设计一个函数仅输出一条线。

设计主函数调用两个函数。

#include

intsumx(inta,intb,intc)/*计算三个整数之和的函数*/

{ints;

s=a+b+c;

returns;

}

voiddisplay()/*输出一条线的函数*/

{printf(”----------------------\n“);

}

voidmain()

{intx,y,z,sa;

x=y=z=2;

display();/*画一条线*/

printf(“\nsum=%d”,sumx(x,y,z));/*在输出语句中直接调用函数sumx()*/

printf(“\n%6d%6d%6d”,x,y,z);

printf(“\n");

display();

x=15;y=16;z=17;

sa=sumx(x,y,z);/*在赋值语句中调用函数sumx()*/

printf(“\n“sum=%d”,sa);

printf(“\n%6d%6d%6d\n”,x,y,z);

display();}/*程序结束*/

运行结果:

----------------------

sum=6

222

----------------------

sum=48

151617

----------------------

2.关于函数的参数传递

函数在被调用时,由主调程序提供实参,将信息传递给形参。

在调用结束后,有时形参可以返回新的数据给主调程序。

这就是所谓参数传递。

各种算法语言实现参数传递的方法通常分为传值和传址两大类。

在上例中函数sumx()的设计和主函数对它的调用,就是传值调用。

第一、第二次调用,带入的实参均是三个整型变量。

调用函数返回后,在主程序中输出实参的值仍与调用之前相同。

传值调用的主要特点是数据的单向传递,由实参通过形参将数据代入被调用函数,不论在调用期间形参值是否改变,调用结束返回主调函数之后,实参值都不会改变。

在不同的算法语言中,传址调用的语法有所不同。

在PASCAL语言中用变参实现传址。

在C语言中采用指针变量做形参来实现传址。

传址调用的主要特点是可以实现数据双向传递,在调用时实参将地址传给形参,该地址中的数据代入被调用函数。

如果在调用期间形参值被改变,也即该地址中的数据发生变化,调用结束返回主调函数之后,实参地址仍然不变,但是该地址中的数据发生相应改变。

这就是数据的双向传递。

现看一例题:

例3设计一个函数实现两个数据的交换,在主程序中调用。

#include

viodswap(int*a,int*b);/*函数原型声明*/

voidmain()

{intx=100,y=800;

printf(“\n%6d%6d”,x,y);/*输出原始数据*/

swap(&x,&y);/*调用交换数据的函数swap()*/

printf(“\n%6d%6d”,x,y);/*输出交换后的数据*/

}

viodswap(int*a,int*b)

{intc;

c=*a;*a=*b;*b=c;

}

运行结果:

100800

800100

实践证明x,y的数据在调用函数前后发生了交换变化。

形参是指向整形的指针变量a和b,在函数体内需要交换的是指针所指的存储单元的内容,因此使用*a=*b;这样的写法。

在调用时,要求实参个数、类型位置与形参一致。

因为实参应该是指针地址,所以调用语句swap(&x,&y)中,实参&x,和&y代入的是整型变量x,y的地址。

在函数体内交换的是实参地址中的内容,而作为主函数变量x,y的地址仍然没有改变。

从整数交换的角度看,本例题实现了双向数据传递。

若从指针地址角度看,调用前后指针地址不变。

在数据结构教材中,很多函数的形参前带有“&”,如顺序表的初始化函数的声明:

StatusInitList_Sq(SqList&L)

其中,形参前带有“&”,说明形参T是引用类型的。

引用类型是C++语言特有的。

引用类型的变量,其值若在函数中发生变化,则变化的值会带回主调函数中。

下面的例子说明了函数中引用类型的变量和非引用类型的变量的区别:

例4

#include

voidfa(inta)//函数中改变a,将不会带回主调函数(主调函数中的a仍是原值)

{a++;

printf("在函数fa中:

a=%d\n",a);

}

voidfb(int&a)//由于a为引用类型,在函数中改变a,其值将带回主调函数

{a++;

printf("在函数fb中:

a=%d\n",a);

}

voidfc(int*a)//a为指针类型,数据双向传递,在函数中改变*a的值,其结果将带回主调函数

{(*a)++;

printf("在函数fc中:

*a=%d\n",*a);

}

voidmain()

{intn=1,*p;

p=&n;

printf("在主程中,调用函数fa之前:

n=%d\n",n);

fa(n);

printf("在主程中,调用函数fa之后,调用函数fb之前:

n=%d\n",n);

fb(n);

printf("在主程中,调用函数fb之后,调用函数fc之前:

n=%d\n",n);

fc(p);

printf("在主程中,调用函数fc之后:

n=%d\n",n);

}

运行结果:

在主程中,调用函数fa之前:

n=1

在函数fa中:

a=2

在主程中,调用函数fa之后,调用函数fb之前:

n=1

在函数fb中:

a=2

在主程中,调用函数fb之后,调用函数fa之前:

n=2

在函数fc中:

*a=3

在主程中,调用函数fc之后,n=3

需要进一步说明的是,C语言的传址调用比较复杂,不如C++的引用调用方便。

因此建议大家采用VisualC++作为编译环境,在此环境下可以兼容C程序,并且可以直接使用引用参数,算法变为程序的过程就会容易很多。

三、结构体及运用

数据结构课程所研究的问题均运用到“结构体”。

在C语言中结构体的定义、输入/输出是数据结构程序设计的重要语法基础。

定义结构体的一般格式:

struct结构体类型名

{类型名1变量名1;//数据子域

类型名2变量名2;……

类型名n变量名n;

};

其中struct是保留字。

结构体类型名由用户自己命名。

在使用时必须声明一个具体的结构体类型的变量,声明创建一个结构体变量的方法是:

struct结构体类型名结构体变量名;

例如:

structElemType/*定义结构体*/

{intnum;charname[10];

};

structElemTypex;/*声明结构体变量x*/

另外有一种方法使用typedef语句定义结构体,在声明结构体变量时可以不写struct,使得书写更加简便。

例如:

typedefstruct

{intnum;

charname[10];

}ElemType;

ElemType就是一个新的类型名,并且是结构体类型名。

声明变量x的语句是:

ElemTypex;

一个结构体中可以包含多个数据子域。

数据子域的类型名一般指基本数据类型(intchar等),也可是已经定义的另一结构体名。

数据子域变量名可以是简单变量,也可以是数组。

它们也可以称为结构体的数据成员。

通过“结构体变量名.数据子域”可以访问数据子域。

例5设计Student结构体,在主程序中运用。

#include

#include

typedefstruct/*定义结构体Student*/

{longnum;/*学号*/

intx;/*成绩*/

charname[10];/*姓名*/

}Student;

voidmain()

{Students1;/*声明创建一个结构体变量s1*/

s1.num=1001;/*为s1的数据子域提供数据*/

s1.x=83;

strcpy(s1.name,“李明”);

printf(“\n姓名:

%s”,s1.name);/*输出结构体变量s1的内容*/

printf(“\n学号:

%d”,s1.num);

printf(“\n成绩:

%d”,s1.x);

}

或者使用键盘输入:

{scanf(“%d”,s1.num);

scanf(“%d”,s1.x);

scanf(“%s”,s1.name);

}

还可以通过“结构体指针->数据子域”来访问数据域。

在实际问题中还会使用到指向结构体的指针,通过以下语句段可以说明结构体指针的一般用法。

{Student*p;/*声明指针变量p*/

p=(Student*)malloc(sizeof(Student));/*分配存储单元,首地址赋给p指针*/

p->num=101;p->x=83;strcpy(p->name,“李明”);

printf(“\n%10s%6d%6d”,p->name,p->num,p->x);

}

设计一个一维数组,每个数组元素是Student结构体类型,通过以下语句段可以说明结构体数组的一般用法。

可以通过“结构体数组名[下标].数据子域”访问数据域。

{Studenta[5];/*声明创建一个结构体数组a*/

inti;

for(i=0,i<5,i++){

printf(“\n学号:

%d”,a[i].num);

printf(“\n姓名:

%s”,a[i].name);

printf(“\n成绩:

%d”,a[i].x);

}

}

四动态分配函数

以数组为例,在我们声明一个数组时,必须用一个常量指定数组的长度,但是常常这个数组的长度是未知的。

一般采用的方法是声明一个较大的数组,内存利用率有可能不高且仍可能溢出。

采用动态分配函数就可以较好地解决这个问题。

在数据结构实验中,很多顺序存储结构都采用的动态分配内存的方式。

不同的C编译系统提供的动态存储分配函数不同。

有的编译系统放在malloc.h中,有的编译系统放在stdlib.h中。

常用的有如下几种:

(1)malloc()函数

其函数原型为:

void*malloc(unsignedintsize);

其功能是:

分配一块长度为size字节的连续空间,并将该空间的首地址作为函数的返回值。

如果函数没有成功执行,返回值为空指针(NULL或0)。

由于返回的指针的基类型为void,应该通过强制类型转换后才能存入其他基类型的指针变量中,否则会有警告提示。

例如:

p=(int*)malloc(sizeof(int));

sizeof运算符返回某类型所需的内存字节数或某变量所分配的字节数,该处返回一个整型变量所需要的字节数2,它即为动态分配内存空间的大小。

返回的指针先要通过(int*)转换成整型指针,然后才赋值给整型指针变量P。

(2)realloc()函数

其函数原型为:

void*realloc(void*ptr,unsignedintsize);

其功能是:

用来改变已分配的内存空间的大小,如果重新分配成功,则该函数返回指向被分配内存的指针,否则返回空指针NULL。

其中,realloc是该函数的名字,ptr是指向已分配的内存空间的指针,即为所要改变大小的内存空间,size是改变后的内存空间大小。

既可以将原来空间变大,也可以将原来空间变小。

如果用于扩大一个内存块,那么这块内存原先的内容依然保留,新增的内存添加到原先内存块的后面。

相反如果用于缩小一个内存块,那么这个内存原来尾部的内存便被删除,但是剩余部分内存的原先内容依然保留。

如果原先内存块的大小是无法改变的那么realloc将重新分配另一块指定大小的内存块,并把原先那块内存的内容复制到新的内存块上面。

所以在使用了realloc之后便不可以再使用原来指向那块旧内存的指针了,而应该使用realloc所返回的新指针。

否者很容易出错!

例如:

p=(int*)malloc(p,20*sizeof(int));

作用是将原来分配给指针p的内存变更为20个整型变量所需内存空间。

(3)free()函数

其函数原型为:

voidfree(void*p);

其功能是:

释放以前分配给指针变量p的动态空间(由函数malloc或realloc等分配的地址)。

例如:

free(p);

作用就是将上例中malloc或realloc分配的空间释放掉。

第二部分上机实验

上机实验要求及规范

一、实验步骤

数据结构课程具有比较强的理论性,同时也具有较强的可应用性和实践性。

上机实验是一个重要的教学环节。

一般情况下学生对于编写程序上机练习具有一定的积极性,但容易忽略实验的总结,忽略实验报告的撰写。

正确的实验步骤如下:

1.问题分析与系统结构设计

充分分析和理解问题,弄清要求做什么(而不是怎么做),限制条件是什么。

按照以数据结构为中心的原则划分模块,搞清数据的逻辑结构(是线性表还是树、图?

),确定数据的存储结构(是顺序结构还是链表结构?

)然后设计有关操作的函数。

在每个函数模块中,要综合考虑系统功能,使系统结构清晰、合理、简单和易于调试。

最后写出每个模块的算法头和规格说明,列出模块之间的调用关系(可以用图表示),便完成了系统结构设计。

2.详细设计和编码

详细设计是对函数(模块)的进一步求精,用伪高级语言(如类C语言)或自然语言写出算法框架,这时不必确定很多结构和变量。

编码,即程序设计,是对详细设计结果的进一步求精,即用某种高级语言(如C/C++语言)表达出来。

尽量多设一些注释语句,清晰易懂。

尽量临时增加一些输出语句,便于差错矫正,在程序成功后再删去它们。

3.上机准备

熟悉高级语言用法,如C语言。

熟悉机器(即操作系统和编译系统),基本的常用命令。

静态检查主要有两条路径,一是用一组测试数据手工执行程序(或分模块进行);二是通过阅读或给别人讲解自己的程序而深入全面地理解程序逻辑,在这个过程中再加入一些注释和断言。

如果程序中逻辑概念清楚,后者将比前者有效。

4.上机调试程序

调试最好分块进行,自底向上,即先调试底层函数,表面上的麻烦工作可以大大降低调试时所面临的复杂性,提高工作效率。

5.整理实习报告

在上机实开始之前要充分准备实验数据,在上机实践过程中要及时记录实验数据,在上机实践完成之后必须及时总结分析。

写出实验报告。

二、实验报告的基本要求:

一般性、较小规模的上机实验题,必须遵循下列要求,养成良好的习惯:

(1)题目:

内容叙述

(2)程序清单(带有必要的注释)

(3)调试报告:

实验者必须重视这一环节,否则等同于没有完成实验任务。

这里可以体现个人特色、或创造性思维。

具体内容包括:

测试数据与运行记录;调试中遇到的主要问题,自己是如何解决的;经验和体会等。

三、实验习报告的提高要求:

阶段性、较大规模的上机实验题,应该遵循下列要求。

养成科学的习惯。

(1)需求和规格说明

描述问题,简述题目要解决的问题是什么,规定软件做什么,原题条件不足时补全。

(2)设计

a.设计思想:

存储结构(题目中限定的要描述);主要算法基本思想。

b.设计表示:

每个函数的头和规格说明;列出每个函数所调用和被调用的函数,也可以通过调用关系图表达。

c.实现注释:

各项功能的实现程度、在完成基本要求的基础上还有什么功能。

(3)用户手册:

使用说明。

(4)调试报告:

调试过程中遇到的主要问题是如何解决的;设计的回顾、讨论和分析;时间复杂度、空间复杂度分析;改进设想;经验和体会等。

四、关于实验题目

本实验手册共分七个大实验,每个实验中又有几个小题目。

学生可以根据实际情况选做其中2个或全做。

有的实验只有一个题目,要求必做。

 

实验一线性表

一、实验目的

1.掌握线性表的逻辑结构特性以及在计算机内的两种存储结构。

2.掌握线性表的基本操作在两种存储结构上的实现;其中以链表的操作为侧重点;会灵活运用线性表结构解决某些实际问题。

二、实例:

线性表的顺序表示(顺序表)及操作实现。

阅读下列程序请注意几个问题:

(1)关于线性表

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1