专题数据结构分类与抽象数据类型Word文档下载推荐.docx
《专题数据结构分类与抽象数据类型Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《专题数据结构分类与抽象数据类型Word文档下载推荐.docx(11页珍藏版)》请在冰豆网上搜索。
01
万明华
男
1962.03.20
处长
02
赵宁
1968.06.14
科长
教材科
03
张利
女
1964.12.07
考务科
04
赵书芳
1972.08.05
主任
办公室
05
刘永年
1959.08.15
科员
06
王明理
1975.04.01
07
王敏
1972.06.28
08
张才
1967.03.17
09
马立仁
1975.10.12
10
邢怀常
1976.07.05
【例1.1】一种数据构造的二元组表示为set=(K,R),其中
K={01,02,03,04,05,06,07,08,09,10}
R={}
在数据构造set中,只存在有元素的集合,不存在有关系,或者说关系为空。
这说明只考虑表中的每条记录,不考虑它们之间的任何关系。
把具有此种特点的数据构造称为集合构造。
集合构造中的元素可以任意排列,无任何次序。
【例1.2】一种数据构造的二元组表示为linearity=(K,R),其中
K={01,02,03,04,05,06,07,08,09,10}
R={<
05,01>
<
01,03>
03,08>
08,02>
02,07>
07,04>
<
04,06>
06,09>
09,10>
}
对应的图形表示如图1.1所示。
图1.1数据的线性构造示意图
结合表1.1,细心的读者不难看出:
R是按职员年龄从大到小排列的关系。
在数据构造linearity中,数据元素之间是有序的,每个数据元素有且仅有一个直接前驱元素〔除构造中第一个元素05外〕,有且仅有一个直接后继元素〔除构造中最后一个元素10外〕。
这种数据构造的特点是数据元素之间的1对1〔1∶1〕联络,即线性关系。
我们把具有这种特点的数据构造叫做线性构造。
【例1.3】一种数据构造的二元组表示为tree=(K,R),其中
R={<
01,02>
01,04>
02,05>
02,06>
03,07>
<
03,09>
04,10>
对应的图形表示如图1.2所示。
图1.2数据的树构造示意图
R是职员之间指导与被指导的关系。
图1.2像倒着画的一棵树,在这棵树中,最上面的一个没有前驱只有后继的结点叫做树根结点,最下面一层的只有前驱没有后继的结点叫做树叶结点,除树根和树叶之外的结点叫做树枝结点。
在一棵树中,每个结点有且只有一个前驱结点〔除树根结点外〕,但可以有任意多个后继结点〔树叶结点可看作为含0个后继结点〕。
这种数据构造的特点是数据元素之间的1对N〔1∶N〕联络〔N≥0〕,即层次关系,我们把具有这种特点的数据构造叫做树构造,简称树。
【例1.4】一种数据构造的二元组表示为graph=(K,R),其中
K={01,02,03,04,05,06,07}
02,01>
04,01>
02,03>
03,02>
06,02>
07,02>
07,03>
06,04>
05,07>
07,05>
对应的图形表示如图1.3所示。
从图1.3可以看出,R是K上的对称关系。
为了简化起见,我们把〈x,y〉和〈y,x〉这两个对称序偶用一个无序对〔x,y〕或〔y,x〕来代替;
在示意图中,我们把x结点和y结点之间两条相反的有向边用一条无向边来代替。
这样R关系可改写为:
R={(01,02),(01,04),(02,03),(02,06),(02,07),
(03,07),(04,06),(05,07)}
对应的图形表示如图1.4所示。
假如说R中每个序偶里的两个元素所代表的职员是好友的话,那么R关系就是人员之间的好友关系。
图1.3数据的图构造示意图图1.4图1.3的等价表示
从图1.3或1.4可以看出,结点之间的联络是M对N〔M∶N〕联络〔M≥0,N≥0〕,即网状关系。
也就是说,每个结点可以有任意多个前驱结点和任意多个后继结点。
我们把具有这种特点的数据构造叫做图构造,简称图。
从图构造、树构造和线性构造的定义可知,树构造是图构造的特殊情况〔即M=1的情况〕,线性构造是树构造的特殊情况〔即N=1的情况〕。
为了区别于线性构造,我们把树构造和图构造统称为非线性构造。
集合构造是整个数据构造中的一种特殊情况,其元素之间不存在任何关系。
【例1.5】一种数据构造的二元组表示为B=(K,R),其中
K={k1,k2,k3,k4,k5,k6}
R={R1,R2}
R1={<
k3,k2>
k3,k5>
k2,k1>
k5,k4>
k5,k6>
R2={<
k1,k2>
k2,k3>
k3,k4>
k4,k5>
假设用实线表示关系R1,虚线表示关系R2,那么对应的图形表示如图1.5所示。
图1.5带有两个关系的一种数据构造示意图
从图1.5可以看出:
数据构造B是图构造。
但是,假设只考虑关系R1那么为树构造,假设只考虑关系R2那么为线性构造。
下面简要讨论数据的存储构造。
存储数据不仅要存储数据中的每个数据元素,而且要存储元素之间的逻辑关系。
详细地说,假设数据是集合构造,那么只需要存储所有数据元素,不需要存储它们之间的任何关系;
假设数据是线性构造、树构造或图构造,那么除了要存储所有数据元素外,还要相应存储元素之间的线性关系、层次关系或网状关系。
数据的存储构造分为顺序、链接、索引和散列4种。
顺序存储对应一块连续的存储空间,该空间的大小要大于等于存储所有元素需占有的存储空间的大小,存储元素之间的联络〔即逻辑构造〕通常不需要附加空间,而是通过元素下标之间的对应关系反映出来,只要简单的计算就可以得到一个元素的前驱或后继元素的下标。
顺序存储空间一般需要通过定义数组类型和数组对象来实现。
在链接存储构造中,元素之间的逻辑关系通过存储结点之间的链接关系反映出来,每个存储结点对应存储一个元素,同时存储该元素的前驱和后继元素所在结点的存储位置,或者说同时存储指向其前驱元素结点和后继元素结点的指针,通过这些指针可以直接访问到其前驱元素和后继元素。
链接存储空间通过定义元素的存储结点类型和对象来实现,所有存储结点可以占用连续的存储空间〔即数组空间〕,也可以占用不连续的存储空间,此空间是由动态分配的每个结点的空间形成的。
索引存储是首先把所有数据元素按照一定的函数关系划分成假设干个子表,每个子表对应一个索引项,然后采用一种存储构造存储所有子表的索引项和采用另一种存储构造存储所有子表中的元素。
如存储汉字字典时,需要采用索引存储,首先按偏旁部首划分所存汉字为假设干子表,得到偏旁部首表,对于每个部首再按所属汉字的笔画多少划分子表,得到检字表,检字表中的每个汉字对应汉字解释表〔即字典主体〕中的一个条目;
然后再分别存储部首表、检字表和汉字解释表。
这里检字表是汉字解释表的索引,而偏旁部首表又是检字表的索引,它是汉字解释表的二级索引。
当存储的数据量很大时,通常都需要采用索引存储,并且时常使用多级索引。
在索引存储中,各级索引表和主表〔即数据元素表〕通常都以文件的形式保存在外存磁盘上,访问任一数据元素时,都要根据该数据元素的特征依次访问各级索引表和最后访问主表,存取外存的次数至少等于建立索引的级数加1。
散列存储方法是按照数据元素的关键字通过一种函数变换直接得到该元素存储地址的方法,该存储地址为相应数组空间中的下标位置。
用于散列存储所有数据元素的相应数组空间称为散列表。
通过定义用于计算散列存储地址的函数和定义存储数据元素的散列表可以实现散列存储构造。
以上简要表达了数据构造的有关概念,在以后的各专题中将会做深化和详细的讨论。
1.2抽象数据类型
抽象数据类型〔AbstractDataType,ADT〕由一种数据构造和在该数据构造上的一组操作所组成。
抽象数据类型包含一般数据类型的概念,但含义比一般数据类型更广、更抽象。
一般数据类型由详细语言系统内部定义,直接提供应编程者定义用户数据,因此称它们为预定义数据类型。
抽象数据类型通常由编程者定义,包括定义它所使用的数据、数据构造以及所进展的操作。
在定义抽象数据类型中的数据局部〔含数据构造在内〕和操作局部时,可以只定义数据的逻辑构造和操作说明,不考虑详细的存储构造和操作的详细实现,这样可以为用户提供一个简明的使用接口,然后再另外给出详细的存储构造和操作的详细实现,使得操作声明与实现分开,从而符合面向对象的程序设计思想。
抽象数据类型在C++语言中是通过类类型来描绘的,其数据局部通常定义为类的私有或保护的数据成员,它只允许该类或派生类直接使用,操作局部通常定义为类的公共的成员函数,它既可以提供应该类或派生类使用也可以提供应外部定义的类和函数使用。
在本书中,为了便于表达和分析数据构造和算法,使读者容易理解和承受,所以在实现所定义的抽象数据类型时,把数据局部用一种的数据类型〔如构造或数组等〕来实现,把操作局部中的每个操作用普通函数来实现,这样可以同读者熟悉的C语言、C++语言,甚至其他计算机语言很好地兼容起来。
一种抽象数据类型的定义将采用如下书写格式:
ADT<
抽象数据类型名>
is
Data:
数据描绘>
Operations:
操作声明>
end<
【例1.6】假定把矩形定义为一种抽象数据类型,其数据局部包括矩形的长度和宽度,操作局部包括初始化矩形的尺寸、求矩形的周长和求矩形的面积。
假定该抽象数据类型名用RECtangle〔矩形〕表示,定义矩形长度和宽度的数据用length和width表示,并假定其类型为浮点〔float〕型,初始化矩形数据的函数名用InitRectangle表示,求矩形周长的函数名用Circumference〔周长〕表示,求矩形面积的函数名用Area〔面积〕表示,那么矩形的ADT〔抽象数据类型〕描绘如下:
ADTRECtangleis
一个矩形r,其长度和宽度分别用length和width表示
//初始化矩形r的长度和宽度值为len和wid
voidInitRectangle(Rectangle&
r,floatlen,floatwid);
//求矩形r的周长并返回
floatCircumference(Rectangle&
r);
//求矩形r的面积并返回
floatArea(Rectangle&
endRECtangle
这里假定数据局部的矩形r是类型名为Rectangle的一个构造对象,该类型的详细定义如下:
structRectangle{
floatlength,width;
};
下面给出每个操作的详细实现。
〔1〕初始化矩形尺寸
voidInitRectangle(Rectangle&
r,floatlen,floatwid){
r.length=len;
//把len值赋给r的length域
r.width=wid;
//把wid值赋给r的width域
该函数把两个值参len和wid的值分别赋给引用参数r的length域和width域,实现对一个矩形r的初始化。
〔2〕求矩形周长
r){
return2*(r.length+r.width);
}
〔3〕求矩形面积
求矩形周长和面积的函数分别使用一个矩形引用参数,当然也可以改为值参。
【例1.7】把二次多项式ax2+bx+c设计成一种抽象数据类型,假定起名为QUAdratic,该类型的数据局部为三个系数项a,b和c,操作局部为:
〔1〕初始化a,b和c的值,假定它们的默认值均为0;
〔2〕做两个多项式加法,返回它们的和;
〔3〕根据给定x的值计算多项式的值并返回;
〔4〕计算方程ax2+bx+c=0的两个实数根,对于有实根、无实根和不是二次方程〔即a==0〕这三种情况都要返回不同的整数值,以便返回后做不同的处理;
〔5〕按照ax**2+bx+c的格式输出二次多项式,在输出时要注意去掉系数为0的项,并且当b和c的值为负时,其前不能出现加号。
该抽象数据类型可详细定义如下:
ADTQUAdraticis
一个二次多项式q,其二次项、一次项和常数项的系数分别用a,b,c表示
//初始化二项式,用于赋给a,b,c值的每个对应参数的默认值设为0
voidInit(Quadratic&
q,floataa=0,floatbb=0,floatcc=0);
//两个二项式相加
QuadraticAdd(Quadratic&
q1,Quadratic&
q2);
//二项式求值
floatEval(Quadratic&
q,floatx);
//求二项式方程的根,两个实根由引用参数r1和r2带回
intRoot(Quadratic&
q,float&
r1,float&
r2);
//输出二项式
voidPrint(Quadratic&
q);
endQUAdratic
这里假定数据局部的二次多项式q是类型名为Quadratic的一个构造对象,该类型的详细定义如下:
structQuadratic{
floata,b,c;
〔1〕初始化a,b和c的值
q,floataa,floatbb,floatcc)
{
q.a=aa;
q.b=bb;
q.c=cc;
〔2〕做两个多项式加法,返回它们的和
q2)
Quadraticq;
q.a=q1.a+q2.a;
q.b=q1.b+q2.b;
q.c=q1.c+q2.c;
returnq;
〔3〕根据给定x的值计算多项式的值并返回
floatEval(Quadratic&
q,floatx)
return(q.a*x*x+q.b*x+q.c);
〔4〕求二项式方程的根,两个实根由引用参数r1和r2带回
r2)
if(q.a==0)return-1;
//不是二次方程返回-1
if(x>
=0){
r1=float(-q.b+sqrt(x))/(2*q.a);
//sqrt(x)为计算x的平方根,该函数存在于系统头文件math.h中
r2=float(-q.b-sqrt(x))/(2*q.a);
return1;
//有实根返回1
elsereturn0;
//无实根返回0
〔5〕输出二项式
q)
if(q.a)cout<
q.a<
"
x**2"
;
if(q.b){
if(q.b>
0)cout<
+"
q.b<
x"
elsecout<
if(q.c){
if(q.c>
q.c;
cout<
endl;
假定使用如下调试程序:
#include<
iostream.h>
math.h>
structQuadratic{
};
//初始化二项式
//两个二项式相加
//二项式求值
//求二项式方程的根,两个实根又引用参数r1和r2带回
//输出二项式
//各函数定义已经在上面给出,这里从略
voidmain()
{
Quadratica,b,c;
Init(a,2,5);
Print(a);
Eval(a,4)<
floatx1,x2;
intn=Root(a,x1,x2);
if(n==1)cout<
x1<
'
'
x2<
Init(b,3,-8,4);
Print(b);
n=Root(b,x1,x2);
c=Add(a,b);
Print(c);
程序运行结果如下:
2x**2+5x
52
0-2.5
3x**2-8x+4
20.666667
5x**2-3x+4