考研 期末 数据结构 第一章 绪论1.docx
《考研 期末 数据结构 第一章 绪论1.docx》由会员分享,可在线阅读,更多相关《考研 期末 数据结构 第一章 绪论1.docx(21页珍藏版)》请在冰豆网上搜索。
考研期末数据结构第一章绪论1
第一章绪论
1.1什么是数据结构
一般来讲,用计算机解决一个具体问题时,大致需要下列几个步骤:
首先从具体问题抽象出一个适当的数学模型,然后设计一个解此数学模型的算法,最后编出程序、进行测试、调整直至得到最终答案。
例1-1图书馆的书目检索系统自动化问题
001
高等数学
樊映川
S01
…
002
理论力学
罗远祥
L01
…
003
高等数学
华罗庚
S01
…
004
线性代数
栾汝书
S02
…
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
高等数学
001,003…
樊映川
001,…
L
002,…
理论力学
002,…
华罗庚
003,…
S
001,003,…
线性代数
004,…
栾汝书
004,…
…
…
…
由这四张表构成的文件便是书目自动检索的数学模型,计算机的主要操作便是按照某个特定要求对书目文件进行查询。
在这类文档管理的数学模型中,计算机处理的对象之间通常存在着的是一种最简单的线性关系,这类数学模型可称为线性的数据结构。
例1-2计算机和人对弈
算法:
?
对弈的规则和策略
模型:
?
棋盘及棋盘的格局
例1-3多叉路口交通灯的管理问题
算法:
?
需要管理的项目?
如何管理?
用户界面?
模型:
?
各种图
概括地说:
数据结构是一门讨论“描述现实世界实体的数学模型(非数值计算)及其上的操作在计算机中如何表示和实现”的学科。
或者说,
数据结构是一门研究非数值计算的程序设计问题中计算机的操作对象以及它们之间的关系和操作等的学科。
数据结构的发展历史
v1968年美国唐欧克努特教授开创了数据结构的最初体系
v从20世纪70年代中期到80年代初,各种版本的数据结构著作就相继出现。
v未来发展的两个方向:
(1)面向各专门领域中特殊问题的数据结构
(2)抽象数据类型的观点来讨论数据结构
1.2基本概念和术语
一、数据与数据结构
数据:
所有能输入到计算机中,且能被计算机程序处理的符号的总称。
是计算机操作的对象的总称。
是计算机处理的信息的某种特定的符号表示形式。
数据元素:
是数据(集合)中的一个“个体”,是数据结构中讨论的基本单位。
可由若干个数据项组成。
数据项:
是数据结构中讨论的最小单位。
数据元素可以是数据项的集合。
例如:
描述一个运动员的数据元素可以是
数据对象:
是性质相同的数据元素的集合,是数据的一个子集。
例如,整数数据对象是集合N={0,1,-1,2,-2,…},字母字符数据对象是集合C={‘A’,‘B’,…,‘Z’}。
数据结构:
带结构的数据元素的集合。
是相互之间存在一种或多种特定关系的数据元素的集合。
假设用三个4位的十进制数表示一个含12位数的十进制数。
例如:
3214,6587,9345─a1(3214),a2(6587),a3(9345)
则在数据元素a1、a2和a3之间存在着“次序”关系a1,a2、a2,a3
3214,6587,9345≠6587,3214,9345
a1a2a3a2a1a3
又例,在2行3列的二维数组{a1,a2,a3,a4,a5,a6}中六个元素之间存在两个关系:
行的次序关系:
row={,,,}
列的次序关系:
col={,,}
a1a3a5≠a1a2a3
a2a4a6a4a5a6
再例,在一维数组{a1,a2,a3,a4,a5,a6}的数据元素之间存在如下的次序关系:
{|i=1,2,3,4,5}
可见,不同的“关系”构成不同的“结构”。
或者说,数据结构是相互之间存在着某种逻辑关系的数据元素的集合。
数据的逻辑结构可归结为以下四类:
线性结构
树形结构
图状结构或网状结构
集合结构
数据结构的形式定义为:
数据结构是一个二元组:
Data_Structures=(D,S)
其中:
D是数据元素的有限集,
S是D上关系的有限集
数据的存储结构:
逻辑结构在存储器中的映象
“数据元素”的映象?
“关系”的映象?
数据元素的映象方法:
例:
用二进制位(bit)的位串表示数据元素
(321)10=(501)8=(101000001)2
A=(101)8=(001000001)2
关系的映象方法:
(表示x,y的方法)
顺序映象
以相对的存储位置表示后继关系。
例如:
令y的存储位置和x的存储位置之间差一个常量C。
而C是一个隐含值,整个存储结构中只含数据元素本身的信息。
链式映象
以附加信息(指针)表示后继关系。
需要用一个和x在一起的附加信息指示y的存储位置。
在不同的编程环境中,存储结构可有不同的描述方法。
当用高级程序设计语言进行编程时,通常可用高级编程语言中提供的数据类型描述之。
例如:
以三个带有次序关系的整数表示一个长整数时,可利用C语言中提供的整数数组类型。
定义长整数为:
typedefintLong_int[3]
二、数据类型
在用高级程序语言编写的程序中,必须对程序中出现的每个变量、常量或表达式,明确说明它们所属的数据类型。
例如,C语言中提供的基本数据类型有:
整型int
浮点型float
双精度型double实型(C++语言)
字符型char
逻辑型bool(C++语言)
不同类型的变量,其所能取的值的范围不同,所能进行的操作不同。
数据类型:
是一个值的集合和定义在此集合上的一组操作的总称。
三、抽象数据类型
(AbstractDataType简称ADT)
是指一个数学模型以及定义在此数学模型上的一组操作。
例如,抽象数据类型复数的定义:
ADTComplex{
数据对象:
D={e1,e2|e1,e2∈RealSet}
数据关系:
R1={|e1是复数的实数部分
|e2是复数的虚数部分}
基本操作:
AssignComplex(&Z,v1,v2)
操作结果:
构造复数Z,其实部和虚部
分别被赋以参数v1和v2的值。
DestroyComplex(&Z)
操作结果:
复数Z被销毁。
GetReal(Z,&realPart)
初始条件:
复数已存在。
操作结果:
用realPart返回复数Z的实部值。
GetImag(Z,&ImagPart)
初始条件:
复数已存在。
操作结果:
用ImagPart返回复数Z的虚部值。
Add(z1,z2,&sum)
初始条件:
z1,z2是复数。
操作结果:
用sum返回两个复数z1,z2的和值
}ADTComplex
假设:
z1和z2是上述定义的复数
则Add(z1,z2,z3)操作的结果
即为用户企求的结果
z3=z1+z2
ADT有两个重要特征:
数据抽象
用ADT描述程序处理的实体时,强调的是其本质的特征、其所能完成的功能以及它和外部用户的接口(即外界使用它的方法)。
数据封装
将实体的外部特性和其内部实现细节分离,并且对外部用户隐藏其内部实现细节。
抽象数据类型的描述方法
抽象数据类型可用(D,S,P)三元组表示。
其中:
D是数据对象;
S是D上的关系集;
P是对D的基本操作集。
ADT抽象数据类型名{
数据对象:
〈数据对象的定义〉
数据关系:
〈数据关系的定义〉
基本操作:
〈基本操作的定义〉
}ADT抽象数据类型名
其中基本操作的定义格式为:
基本操作名(参数表)
初始条件:
〈初始条件描述〉
操作结果:
〈操作结果描述〉
赋值参数只为操作提供输入值。
引用参数以&打头,除可提供输入值外,还将返回操作结果。
初始条件描述了操作执行之前数据结构和参数应满足的条件,若不满足,则操作失败,并返回相应出错信息。
操作结果说明了操作正常完成之后,数据结构的变化状况和应返回的结果。
若初始条件为空,则省略之。
例1—6抽象数据类型三元组的定义:
ADTTriplet{
数据对象:
D={e1,e2,e3|e1,e2,e3∈ElemSet(定义了关系运算的某个集合)}
数据关系:
R1={,}
基本操作:
InitTriplet(&T,v1,v2,v3)
操作结果:
构造了三元组T,元素e1,e2和e3分别被赋以参数v1,v2和v3的值。
DestroyTriplet(&T)
操作结果:
三元组T被销毁。
Get(T,I,&e)
初始条件:
三元组已存在,1<=i<=3.
操作结果:
用e返回T的第i元的值.
Put(&T,i,e)
初始条件:
三元组T已存在,1<=i<=3.
操作结果:
改变T的第i元的值为e.
IsAscending(T)
初始条件:
三元组T已存在.
操作结果:
如果T的3个元素按升序排列,则返回1,否则返回0.
IsDescending(T)
初始条件:
三元组已存在.
操作结果:
如果T的3个元素按降序排列,则返回1,否则返回0.
Ma(T,&e)
初始条件:
三元组已存在.
操作结果:
用e返回T的3个元素中的最大值.
}ADT Triplet
多形数据类型
是指其值的成分不确定的数据类型。
例如,例1—6中定义的抽象数据类型Triplet,其元素e1、e2和e3可以是整数或字符或字符串,甚至更复杂的由多种成分构成。
从抽象数据类型的角度看,具有相同的数学抽象特性,故称之为多形数据类型。
1.3抽象数据类型的表示与实现
抽象数据类型需要通过固有数据类型(高级编程语言中已实现的数据类型)来实现。
以下对类C语言的描述作简要说明。
(1)预定义常量和类型
//函数结果状态代码
#defineTRUE1
#defineFALSE0
#defineOK1
#defineERROR0
#defineINFEASIBLE-1
#defineOVERFLOW-2
//Status是函数的类型,其值是函数结果状态代码
TypedefintStatus;
(2)数据结构的表示(存储结构)用类型定义(typedef)描述。
数据元素类型约定
ElemType,由用户在使用该数据类型时自行定义。
(3)基本操作的算法都用以下形式的函数描述:
函数类型函数名(函数参数表){
//算法说明
语名序列
}//函数名
除了函数的参数需要说明类型外,算法中使用的辅助变量可以不作变量说明,必要时对其作用给予注释。
一般而言,a、b、c、d、e等用作数据元素名,i、j、k、l、m、n等用作整形变量名,p、q、r等用作指针变量名。
当函数返回值为函数结果状态代码时,函数定义为Status类型。
为了便于算法描述,除了值调用方式外,增添了C++语言的引用调用的参数传递方式。
在形参表中,以&打头的参数即为引用参数。
(4)赋值语名有
简单赋值变量名=表达式;
串联赋值变量名1=变量名2=…=变量名k=表达式;
成组赋值(变量名1,…,变量名k)=(表达式1,
…,表达式k);
结构名=结构名;
结构名=(值1,…,值k);
变量名[]=表达式;
变量名[起始下标..终止下标]=变量名[起始下标..终止下标];
交换赋值变量名()变量名;
条件赋值变量名=条件表达式?
表达式T:
表达式F;
(5)选择语句有
条件语句1if(表达式)语句;
条件语句2if(表达式)语句;
else语句;
开关语句1switch(表达式){
case值1:
语句序列break;
…
case值n:
语句序列n;break;
default;语句序列n+1;
}
开关语句2switch{
case条件1:
语句序列1;break;
default:
语句序列n+1;
}
(6)循环语句有
for语句for(赋初值表达式序列;条件;修改
表达式序列)语句;
while语句while(条件)语句;
do-while语句do{
语句序列;
}while(条件);
(7)结束语句有
函数结束语句return表达式;
return;
case结束语句break;
异常结束语句exit(异常代码);
(8)输入和输出语句有
输入语句scanf([格式串],变量1,…,变量n);
输出语句printf([格式串],表达式1,…,表达式n);
通常省略格式串。
(9)注释
单行注释//文字序列
(10)基本函数有
求最大值max(表达式1,…,表达式n)
求最小值min(表达式1,…,表达式n)
求绝对值abs(表达式)
求不足整数值floor(表达式)
求进位整数值ceil(表达式)
判定文件结束eof(文件变量)或eof
判定行结束eoln(文件变量)或eoln
(11)逻辑运算约定
与运算&&:
对于A&&B,当A的值为0时,不再对B求值。
或运算||:
对于A||B,当A的值为非0时,不再对B求值。
例如,对以上定义的复数。
//-----存储结构的定义
typedefstruct{
floatrealpart;
floatimagpart;
}complex;
//-----基本操作的函数原型说明
voidAssign(complex&Z,
floatrealval,floatimagval);
//构造复数Z,其实部和虚部分别被赋以参数//realval和imagval的值
floatGetReal(cpmplexZ);
//返回复数Z的实部值
floatGetimag(cpmplexZ);
//返回复数Z的虚部值
voidadd(complexz1,complexz2,complex&sum);
//以sum返回两个复数z1,z2的和
//-----基本操作的实现
voidadd(complexz1,complexz2,
complex&sum){
//以sum返回两个复数z1,z2的和
sum.realpart=z1.realpart+z2.realpart;
sum.imagpart=z1.imagpart+z2.imagpart;
}
{其它省略}
例1-7抽象数据类型Triplet的表示和实现。
//----采用动态分配的顺序存储结构-----
typedefElemType*Triplet;//由InitTriplet分配3个元素存储空间
//----基本操作的函数原型说明-----
StatusInitTriplet(Triplet&T,ElemTypev1,ElemTypev2,ElemTypev3);
//操作结果:
构造了三元组T,元素e1,e2和e3分别被赋以参数v1,v2和v3的值。
StatusDestroyTriplet(Triplet&T);
//操作结果:
三元组T被销毁。
StatusGet(TripletT,inti,ElemType&e);
//初始条件:
三元组已存在,1<=i<=3.
//操作结果:
用e返回T的第i元的值.
StatusPut(Triplet&T,inti,ElemTypee);
//初始条件:
三元组T已存在,1<=i<=3.
//操作结果:
改变T的第i元的值为e.
StatusIsAscending(TripletT);
//初始条件:
三元组T已存在.
//操作结果:
如果T的3个元素按升序排列,则返回1,否则返回0.
StatusIsDescending(TripletT);
//初始条件:
三元组已存在.
//操作结果:
如果T的3个元素按降序排列,则返回1,否则返回0.
StatusMax(TripletT,ElemType&e)
//初始条件:
三元组已存在.
//操作结果:
用e返回T的3个元素中的最大值.
StatusMin(TripletT,ElemType&e)
//初始条件:
三元组已存在.
//操作结果:
用e返回T的3个元素中的最小值.
//----基本操作的函数原型说明-----
StatusInitTriplet(Triplet&T,ElemTypev1,ElemTypev2,ElemTypev3)
{
//构造了三元组T,依次置T的三个元素的初值v1,v2和v3。
T=(ElemType*)malloc(3*sizeof(ElemType));
//分配三个元素的存储空间
if(!
T)exit(OVERFLOW);//分配存储空间失败
T[0]=v1;T[1]=v2;T[2]=v3;
returnOK;
}//InitTriplet
StatusDestroyTriplet(Triplet&T){
//销毁三元组T。
free(T);T=NULL;
returnOK;
}//InitTriplet
StatusGet(TripletT,inti,ElemType&e){
//1<=i<=3,用e返回T的第i元的值.
if(i<1||i>3)returnERROR;
e=T[i-1];
returnOK;
}//Get
StatusPut(Triplet&T,inti,ElemTypee){
//1<=i<=3,置T的第i元的值为e.
if(i<1||i>3)returnERROR;
T[i-1]=e;
returnOK;
}//Put
StatusIsAscending(TripletT){
//如果T的3个元素按升序排列,则返回1,否则返回0.
return(T[0]<=T[1])&&T[1]<=T[2]);
}//IsAscending
StatusIsDescending(TripletT){
//如果T的3个元素按降序排列,则返回1,否则返回0.
return(T[0]>=T[1])&&T[1]>=T[2]);
}//IsDescending
StatusMax(TripletT,ElemType&e){
//用e返回T的3个元素中的最大值.
e=(T[0]>=T[1])?
((T[0]>=T[2])?
T[0]:
T[2]):
((T[1]>=T[2])?
T[1]:
T[2]);
returnOK;
}//Max
StatusMin(TripletT,ElemType&e){
//用e返回T的3个元素中的最小值.
e=(T[0]<=T[1])?
((T[0]<=T[2])?
T[0]:
T[2]):
((T[1]<=T[2])?
T[1]:
T[2]);
returnOK;
}//Min
1.4算法和算法分析
1.4.1算法
算法是为了解决某类问题而规定的一个有限长的操作序列。
一个算法必须满足以下五个重要特性:
1.有穷性2.确定性3.可行性
4.有输入5.有输出
1.有穷性对于任意一组合法输入值,在执行有穷步骤之后一定能结束,即:
算法中的每个步骤都能在有限时间内完成。
2.确定性对于每种情况下所应执行的操作,在算法中都有确切的规定,使算法的执行者或阅读者都能明确其含义及如何执行。
并且在任何条件下,算法都只有一条执行路径,不允许有二义性。
例如下面这句话:
‘张三对李四讲,他的儿子考上了大学。
’
确切含义?
3.可行性算法中的所有操作都必须足够基本,都可以通过已经实现的基本操作运算有限次实现之。
4.有输入作为算法加工对象的量值,通常体现为算法中的一组变量。
有些输入量需要在算法执行过程中输入,而有的算法表面上可以没有输入,实际上已被嵌入算法之中。
5.有输出它是一组与“输入”有确定关系的量值,是算法进行信息加工后得到的结果,这种确定关系即为算法的功能。
1.4.2算法设计的原则
设计算法时,通常应考虑达到以下目标:
1.正确性
2.可读性
3.健壮性
4.高效率与低存储量需求
1.正确性
首先,算法应当满足以特定的“规格说明”方式给出的需求。
其次,对算法是否“正确”的理解可以有以下四个层次:
a.程序中不含语法错误;
b.程序对于几组输入数据能够得出满足要求的结果;
c.程序对于精心选择的、典型、苛刻且带有刁难性的几组输入数据能够得出满足要求的结果;
d.程序对于一切合法的输入数据都能得出满足要求的结果;
通常以第c层意义的正确性作为衡量一个算法是否合格的标准。
2.可读性
算法主要是为了人的阅读与交流,其次才是为计算机执行,因此算法应该易于人的理解;另一方面,晦涩难读的程序易于隐藏较多错误而难以调试。
3.健壮性
当输入的数据非法时,算法应当恰当地作出反映或进行相应处理,而不是产生莫名奇妙的输出结果。
并且,处理出错的方法不应是中断程序的执行,而应是返回一个表示错误或错误性质的值,以便在更高的抽象层次上进行处理。
4.高效率与低存储量需求
通常,效率指的是算法执行时间;存储量指的是算法执行过程中所需的最大存储空间,两者都与问题的规模有关。
1.4.3算法效率的度量
通常有两种衡量算法效率的方法:
事后统计法
缺点:
1.必须执行程序
2.其它因素掩盖算法本质
事前分析估算法
和算法执行时间相关的因素:
1.算法选用的策略
2.问题的规模
3.编写程序的语言
4.编译程序产生的机器代码的质量
5.计算机执行指令的速度
一个特定算法的“运行工作量”的大小,只依赖于问题的规模(通常用整数量n表示),或者说,它是问题规模的函数。
假如,随着问题规模n的增长,算法执行时间的增长率和f(n)的增长率相同,则可记作:
T(n)=O(f(n))
称T(n)为算法的(渐近)时间复杂度。
如何估算算法的时间复杂度?
算法=控制结构+原操作
(固有数据类型的操