数据结构1.docx

上传人:b****8 文档编号:9819422 上传时间:2023-02-06 格式:DOCX 页数:157 大小:155.36KB
下载 相关 举报
数据结构1.docx_第1页
第1页 / 共157页
数据结构1.docx_第2页
第2页 / 共157页
数据结构1.docx_第3页
第3页 / 共157页
数据结构1.docx_第4页
第4页 / 共157页
数据结构1.docx_第5页
第5页 / 共157页
点击查看更多>>
下载资源
资源描述

数据结构1.docx

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

数据结构1.docx

数据结构1

第一章绪论

§1.1什么是数据结构

一.数据结构的概念

NiklausWirth说过Algorithm+DataStructures=Programs,其中程序设计:

为计算机处理问题编制一组指令集,算法:

处理问题的策略,数据结构:

问题的数学模型。

在计算机发展的早期,主要用于数值计算,例如:

结构静力分析计算─━线性代数方程组,全球天气预报─━环流模式方程,但是随着计算机应用领域的扩大和硬、软件技术的发展,非数值计算问题越来越多(占90%)

例1:

学生信息检索,计算机处理的对象之间存在一种简单的线性关系,这类数学模型可以归结为线性数据结构。

例2:

计算机和人对弈(八皇后问题等),为了得到合理的布局,计算机要存储当前的布局,从最初的布局开始,每个布局都会衍生出许多新的布局,这时计算机所要处理的是一颗对弈树,也是一种典型的数据结构。

例3:

教学计划的编排(交通灯),一个教学计划包含许多课程,课程间有些必须按规定的先后顺序进行,有些则没有次序要求,各个课程间的次序关系可用图来表示。

也是一种典型的数据结构

由此可见,描述这类非数值计算问题的数学模型已不再是数学方程,而是诸如表、树、图之类的数据结构,因此说,数据结构主要是研究非数值计算的程序设计中所出现的计算机操作对象以及它们之间关系和操作的学科。

二.数据结构的产生和发展

1968年以独立的学科设立,之后不断发展,目前已成为计算机专业的核心课程和许多非计算机专业的选修课程。

§1.2有关概念和术语

一.数据结构的概念

1.数据:

所有能被输入到计算机中,且被计算机处理的符号的集合计算机操作的对象的总称,是计算机处理的信息的某种特定的符号表示形式

2.数据元素:

数据中的一个“个体”,数据结构中讨论的基本单位,一个数据元素可能包含若干数据项,数据项:

数据结构中讨论的最小单位,数据元素是数据项的集合,例如运动员(数据元素)

姓名俱乐部名称出生日期参加日期职务业绩

其中 出生日期 年 月 日是组合项

3.数据对象:

性质相同的数据元素的集合,是数据的一个子集,

4.数据结构:

是相互之间存在一种或多种特定关系的数据元素的集合,数据元素的关系称为结构。

二.数据结构的表示

数据结构是一个二元组:

Data_Structures=(D,S)

其中:

D是数据元素的有限集,S是D上关系的有限集。

严格地讲,以上定义仅是数据的逻辑结构的定义,逻辑结构可以看作是从一个具体问题抽象出来的数学模型,与数据的存储无关。

数据的逻辑结构可归结为以下四类:

线性结构、树形结构、图状结构、集合结构。

集合结构:

属于同一集合,别无其他关系(常用其他结构表示)

线性结构:

数据元素之间存在一对一的关系

树状结构:

数据元素之间存在一对多的关系

图状结构:

数据元素之间存在多对多的关系

 

 

数据的存储结构─━逻辑结构在存储器中的映象,他所研究的是数据结构在计算机中的实现,主要有顺序存储、链式存储、索引和散列存储

数据元素的映象方法即关系的映象方法:

(表示的方法)

顺序映象以存储位置的相邻表示后继关系,y的存储位置和x的存储位置之间差一个常量C,而C是一个隐含值,整个存储结构中只含数据元素本身的信息,通常用数组来实现。

链式映象以附加信息(指针)表示后继关系,需要用一个和x在一起的附加信息指示y的存储位置,不要求物理位置的相邻。

在不同的编程环境中,存储结构可有不同的描述方法,当用高级程序设计语言进行编程时,通常可用高级编程语言中提供的数据类型描述之。

例如:

以三个带有次序关系的整数表示一个长整数时,可利用C语言中提供的整数数组类型,定义长整数为:

typedefintLong_int[3];

三、数据类型

数据类型:

在用高级程序语言编写的程序中,必须对程序中出现的每个变量、常量或表达式,明确说明它们所属的数据类型。

因为类型明显或隐含地规定了,在程序执行期间,变量或表达式所有可能取值的范围,以及在这些之上允许进行的操作。

数据类型是一个值的集合和定义在此集合上的一组操作的总称。

四.抽象数据类型(AbstractDataType简称ADT)

1.定义:

是指一个数学模型以及定义在此数学模型上的一组操作,和数据类型本质相同,抽象指的是数学模型的数学抽象特性,而且抽象数据类型的范围更广。

ADT有两个重要特征:

数据抽象:

用ADT描述程序处理的实体时,强调的是其本质的特征、其所能完成的功能以及它和外部用户的接口(即外界使用它的方法)。

数据封装:

将实体的外部特性和其内部实现细节分离,并且对外部用户隐藏其内部实现细节。

例如抽象数据类型复数的定义:

ADTComplex{

数据对象:

D={e1,e2|e1,e2∈RealSet}

数据关系:

R1={|e1是复数的实数部分,|e2是复数的虚数部分}

基本操作:

InitComplex(&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

抽象数据类型的描述方法

2.抽象数据类型可用(D,S,P)三元组表示

其中,D是数据对象,S是D上的关系集,P是对D的基本操作集。

ADT抽象数据类型名{

数据对象:

〈数据对象的定义〉

数据关系:

〈数据关系的定义〉

基本操作:

〈基本操作的定义〉

}ADT抽象数据类型名

其中,数据对象和数据关系的定义用伪码描述,基本操作的定义格式为

基本操作名(参数表)

初始条件:

〈初始条件描述〉

操作结果:

〈操作结果描述〉

基本操作有两种参数:

赋值参数只为操作提供输入值;引用参数以&打头,除可提供输入值外,还将返回操作结果。

“初始条件”描述了操作执行之前数据结构和参数应满足的条件,若不满足,则操作失败,并返回相应出错信息。

“操作结果”说明了操作正常完成之后,数据结构的变化状况和应返回的结果。

若初始条件为空,则省略之。

§1.3抽象数据类型的表示和实现:

抽象数据类型需要通过固有数据类型(高级编程语言中已实现的数据类型)来实现

一、预处理:

#defineTRUE1#defineFALSE0#defineOK1#defineERROR0#defineINFEASIBLE-1

#defineOVERFLOW-2

#include

#include

typedefintStatus;

二、C语言中的运算符

()、[]、->、。

~++--+(正)-(负)*(指针)&sizeof(类型)

*/%

+-

<<>>

<=<>>=

!

===

&^|

&&||

=+=-=*=/=%=>>=<<=&=^=|=

(逗号)

§1.4算法和算法的衡量

一、算法

算法是为了解决某类问题而规定的一个有限长的操作序列。

一个算法必须满足以下五个重要特性:

1.有穷性对于任意一组合法输入值,在执行有穷步骤之后一定能结束,即:

算法中的每个步骤都能在有限时间内完成;

2.确定性对于每种情况下所应执行的操作,在算法中都有确切的规定,使算法的执行者或阅读者都能明确其含义及如何执行。

并且在任何条件下,算法都只有一条执行路径;

3.可行性算法中的所有操作都必须足够基本,都可以通过已经实现的基本操作运算有限次实现之;

4.有输入作为算法加工对象的量值,通常体现为算法中的一组变量。

有些输入量需要在算法执行过程中输入,而有的算法表面上可以没有输入,实际上已被嵌入算法之中;

5.有输出它是一组与“输入”与确定关系的量值,是算法进行信息加工后得到的结果,这种确定关系即为算法的功能。

二、算法设计的原则

设计算法时,通常应考虑达到以下目标:

1.正确性

首先,算法应当满足以特定的“规格说明”方式给出的需求。

其次,对算法是否“正确”的理解可以有以下四个层次:

a.程序中不含语法错误;

b.程序对于几组输入数据能够得出满足要求的结果;

c.程序对于精心选择的、典型、苛刻切带有刁难性的几组输入数据能够得出满足要求的结果;

d.程序对于一切合法的输入数据都能得出满足要求的结果;

通常以第c层意义的正确性作为衡量一个算法是否合格的标准。

2.可读性

算法主要是为了人的阅读与交流,其次才是为计算机执行。

因此算法应该易于人的理解;另一方面,晦涩难读的程序易于隐藏较多错误而难以调试;

3.健壮性

当输入的数据非法时,算法应当恰当地作出反映或进行相应处理,而不是产生莫名奇妙的输出结果。

并且,处理出错的方法不应是中断程序的执行,而应是返回一个表示错误或错误性质的值,以便在更高的抽象层次上进行处理。

4.高效率与低存储量需求

通常,效率指的是算法执行时间;存储量指的是算法执行过程中所需的最大存储空间。

两者都与问题的规模有关。

三、算法效率的衡量方法和准则

通常有两种衡量算法效率的方法:

事后统计法

缺点:

1。

必须执行程序2.其它因素掩盖算法本质

事前分析估算法

和算法执行时间相关的因素:

1.算法选用的策略

2.问题的规模

3.编写程序的语言

4.编译程序产生的机器代码的质量

5.计算机执行指令的速度

一个特定算法的“运行工作量”的大小,只依赖于问题的规模(通常用整数量n表示),或者说,它是问题规模的函数。

假如,随着问题规模n的增长,算法执行时间的增长率和f(n)的增长率相同,则可记作:

T(n)=O(f(n)),称T(n)为算法的(渐近)时间复杂度

如何估算算法的时间复杂度?

算法=控制结构+原操作(固有数据类型的操作)

算法的执行时间=原操作(i)的执行次数×原操作(i)的执行时间,算法的执行时间与原操作执行次数之和成正比从算法中选取一种对于所研究的问题来说是基本操作的原操作,以该基本操作,在算法中重复执行的次数作为算法运行时间的衡量准则

例1

for(i=1;i<=n;++i)

for(j=1;j<=n;++j){

c[i,j]=0;

for(k=1;k<=n;++k)

c[i,j]+=a[i,k]*b[k,j];

}

基本操作:

乘法操作,时间复杂度:

O(n3)

例2

voidselect_sort(inta[],intn){

//将a中整数序列重新排列成自小至大有序的整数序列。

for(i=0;i

j=i;

for(k=i+1;k

if(a[k]

if(j!

=i)a[j]←→a[i]

}//select_sort

基本操作:

比较(数据元素)操作,时间复杂度:

O(n2)

四、算法的存储空间需求

算法的空间复杂度:

S(n)=O(g(n))

表示随着问题规模n的增大,算法运行所需存储量的增长率与g(n)的增长率相同。

算法的存储量包括:

1.输入数据所占空间;

2.程序本身所占空间;

3.辅助变量所占空间。

若输入数据所占空间只取决与问题本身,和算法无关,则只需要分析除输入和程序之外的额外空间。

若所需额外空间相对于输入数据量来说是常数,则称此算法为原地工作。

若所需存储量依赖于特定的输入,则通常按最坏情况考虑。

学习要点:

  1.熟悉各名词、术语的含义,掌握基本概念,特别是数据的逻辑结构和存储结构之间的关系。

分清哪些是逻辑结构的性质,哪些是存储结构的性质。

  2.了解抽象数据类型的定义、表示和实现方法。

  3.理解算法五个要素的确切含义:

①动态有穷性(能执行结束);②确定性(对于相同的输入执行相同的路径);③有输入;④有输出;⑤可行性(用以描述算法的操作都是足够基本的)。

 

  4.掌握计算语句频度和估算算法时间复杂度的方法。

第二章线性表

线性结构是一个数据元素的有序(次序)集

线性结构的基本特征:

1.集合中必存在唯一的一个“第一元素”;

2.集合中必存在唯一的一个“最后元素”;

3.除最后元素在外,均有唯一的后继;

4.除第一元素之外,均有唯一的前驱。

§2.1线性表的逻辑结构

一、线性表的类型定义

抽象数据类型线性表的定义如下:

ADTList{

数据对象:

D={ai|ai∈ElemSet,i=1,2,...,n,n≥0}

{称n为线性表的表长;称n=0时的线性表为空表。

}

数据关系:

R1={|ai-1,ai∈D,i=2,...,n}

{设线性表为(a1,a2,...,ai,...,an),称i为ai在线性表中的位序。

}

二、基本操作:

{结构初始化}

1.InitList(&L)

操作结果:

构造一个空的线性表L。

{销毁结构}

2.DestroyList(&L)

初始条件:

线性表L已存在。

操作结果:

销毁线性表L。

{引用型操作}

3.ListEmpty(L)

初始条件:

线性表L已存在。

操作结果:

若L为空表,则返回TRUE,否则FALSE。

4.ListLength(L)

初始条件:

线性表L已存在。

操作结果:

返回L中元素个数。

5.PriorElem(L,cur_e,&pre_e)

初始条件:

线性表L已存在。

操作结果:

若cur_e是L的元素,但不是第一个,则用pre_e返回它的前驱,否则操作失败,pre_e无定义。

6.NextElem(L,cur_e,&next_e)

初始条件:

线性表L已存在。

操作结果:

若cur_e是L的元素,但不是最后一个,则用next_e返回它的后继,否则操作失败,next_e无定义。

7.GetElem(L,cur_e,&next_e)

初始条件:

线性表L已存在,1≤i≤LengthList(L)

操作结果:

用e返回L中第i个元素的值。

8.LocateElem(L,e,compare())

初始条件:

线性表L已存在,compare()是元素判定函数。

操作结果:

返回L中第1个与e满足关系compare()的元素的位序。

若这样的元素不存在,则返回值为0。

9.ListTraverse(L,visit())

初始条件:

线性表L已存在。

操作结果:

依次对L的每个元素调用函数visit()。

一旦visit()失败,则操作失败。

{加工型操作}

10.ClearList(&L)

初始条件:

线性表L已存在。

操作结果:

将L重置为空表。

11.PutElem(L,i,&e)

初始条件:

线性表L已存在,1≤i≤LengthList(L)

操作结果:

L中第i个元素赋值同e的值。

12.ListInsert(&L,i,e)

初始条件:

线性表L已存在,1≤i≤LengthList(L)+1

操作结果:

在L的第i个元素之前插入新的元素e,L的长度增1。

13.ListDelete(&L,i,&e)

初始条件:

线性表L已存在且非空,1≤i≤LengthList(L)

操作结果:

删除L的第i个元素,并用e返回其值,L的长度减1。

}ADTList

利用上述定义的线性表可以完成其它更复杂的操作

例2-1假设有两个集合A和B分别用两个线性表LA和LB表示(即:

线性表中的数据元素即为集合中的成员),现要求一个新的集合A=A∪B。

上述问题可演绎为,要求对线性表作如下操作:

扩大线性表LA,将存在于线性表LB中而不存在于线性表LA中的数据元素插入到线性表LA中去。

1.从线性表LB中依次取得每个数据元素;GetElem(LB,i)→e

2.依值在线性表LA中进行查访;LocateElem(LA,e,equal())

3.若不存在,则插入之。

ListInsert(LA,n+1,e)

voidunion(List&La,ListLb){

//将所有在线性表Lb中但不在La中的数据元素插入到La中

La_len=ListLength(La);

Lb_len=ListLength(Lb);//求线性表的长度

for(i=1;i<=Lb_len;i++){

GetElem(Lb,i,e);

//取Lb中第i个数据元素赋给e

if(!

LocateElem(La,e,equal())

ListInsert(La,++La_len1,e);

//La中不存在和e相同的数据元素,则插入之

}

}//union

例2-2已知一个非纯集合B,试构造一个纯集合A,使A中只包含B中所有值各不相同的数据元素。

voidpurge(List&La,ListLb){

//已知线性表Lb中的数据元素依值非递减有序排列(Lb是有序表),

//构造线性表La,使其只包含Lb中所有值不相同的数据元素

InitList(LA);

La_len=ListLength(La);

Lb_len=ListLength(Lb);//求线性表的长度

for(i=1;i<=Lb_len;i++){

GetElem(Lb,i,e);//取Lb中第i个数据元素赋给e

if(!

equal(en,e)){

ListInsert(La,++La_len,e);

en=e;

}//La中不存在和e相同的数据元素,则插入之

}

}//purge

例2-3归并两个“其数据元素按值非递减有序排列的”线性表LA和LB,求得线性表LC也具有同样特性

设La=(a1,…,ai,…,an)

Lb=(b1,…,bj,…,bm)

Lc=(c1,…,ck,…,cm+n)

则Ck=k=1,2,…,m+n

1.分别从LA和LB中取得当前元素ai和bj;

2.若ai≤bj,则将ai插入到LC中,否则将bj插入到LC中。

voidMergeList(ListLa,ListLb,List&Lc){

//已知线性表La和Lb中的元素按值非递减排列。

//归并La和Lb得到新的线性表Lc,

//Lc的元素也按值非递减排列。

InitList(Lc);

i=j=1;k=0;

La_len=ListLength(La);

Lb_len=ListLength(Lb);

while((i<=La_len)&&(j<=Lb_len)){

//La和Lb均非空

GetElem(La,i,ai);GetElem(Lb,j,bj);

if(ai<=bj){

ListInsert(Lc,++k,ai);++i;}

else{ListInsert(Lc,++k,bj);++j;}

}

while(i<=La_len){

GetElem(La,i++,ai);

ListInsert(Lc,++k,ai);

}

while(j<=Lb_len){

GetElem(Lb,j++,bj);

ListInsert(Lc,++k,bj);

}

}//merge_list

§2.2线性表的顺序存储和基本操作的实现

一、顺序表

用一组地址连续的存储单元依次存放线性表中的数据元素,线性表的起始地址,称作线性表的基地址,以“存储位置相邻”表示有序对即:

所有数据元素的存储位置均取决于第一个数据元素的存储位置

Loc(ai)=Loc(a1)+(I-1)*l,1≤i≤n,l=sizeof(ElemType)

顺序映像的C语言描述

//-----线性表的动态分配顺序存储结构-----

#defineLIST_INIT_SIZE80//线性表存储空间的初始分配量

#defineLISTINCREMENT10//线性表存储空间的分配增量

typedefstruct{

ElemType*elem;//存储空间基址

intlength;//当前长度

intlistsize;//当前分配的存储容量(以sizeof(ElemType)为单位)

}SqList;//俗称顺序表

二、线性表的基本操作的实现

1、初始化在顺序映像中的实现

StatusInitList_Sq(SqList&L){

//构造一个空的线性表L。

L.elem=(ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType));

if(!

L.elem)exit(OVERFLOW);//存储分配失败

L.length=0;//长度为0

L.listsize=LIST_INIT_SIZE;//初始存储容量

returnOK;

}//InitList_Sq

2、线性表的建立

StatusCreaList_Sq(SqList&L){intlength,i,data;printf("输入表的长度\n");scanf("%d",&length);InitList_Sq(L);printf("输入表中数据\n");

for(i=0;i

{

scanf("%d",&data);

L.elem[i]=data;

}

L.length=length;

returnOK;

}//Crea_Sq

3、线性表操作LocateElem(L,e,compare())的实现:

intLocateElem_Sq(SqListL,ElemTypee,Status(*compare)(ElemType,ElemType)){

//在顺序线性表L中查找第1个值与e满足compare()的元素的位序。

//若找到,则返回其在L中的位序,否则返回0。

i=1;//i的初值为第1元素的位序

p=L.elem;//p的初值为第1元素的存储位置

while(i<=L.length&&!

(*compare)(*p++,e))++i;

if(i<=L.length)returni;

else

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

当前位置:首页 > 高中教育 > 其它课程

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

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