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

数据结构9512
第1章绪论
数据结构课程是计算机及相关专业中的一门专业基础课,它介绍和研究数据在计算机中的组织、存储和处理的方法。
这里所说的数据的概念是广义的,它不仅表示单一的数据,如字符、数值等,而且表示带结构的数据,如记录、数组、矩阵、登记表、结构图等。
数据在计算机中的组织和存储方法有顺序、链接、散列、索引等多种,根据数据处理的需要可从中选择一种或几种的组合来存储数据。
对数据进行处理的方法又叫做算法,它是根据数据处理的实际需要而逐渐产生和发展起来的。
现在人们已经总结出进行数据处理的各种具体、实用和有效的算法,根据这些算法和存储在计算机中的数据,再利用一种算法描述语言(如C语言)和面向过程或对象的程序设计方法就能够编写出进行数据处理的程序,通过计算机运行这个程序自动完成特定的数据处理任务。
学习数据结构课程除了要学习和研究已有的数据存储结构和数据处理算法之外,更重要的是根据自己解决实际问题的需要,进行有效的数据存储和数据处理。
1.1基本概念
数据(data)是人们利用文字符号、数字符号以及其他规定的符号对现实世界的事物及其活动所做的抽象描述。
例如,一个人的名字可以用一个字符串来描述,一个图形可以用一个数组来描述,数组中的每一个值用来存储图形中对应点的坐标值。
因此,一个文档、记录、数组、句子、单词、算式、符号等都统称为数据。
在计算机领域,人们把能够被计算机加工的对象,或者说能够被计算机输入、存储、处理和输出的一切信息都叫做数据。
数据元素(dataelement)简称元素,它是一个数据整体中相对独立的单位。
如对于一个文件来说,每个记录就是它的数据元素;对于一个字符串来说,每个字符就是它的数据元素;对于一个数组来说,每一个下标位置上的数据就是它的数据元素。
数据和数据元素是相对而言的,如对于一个记录来说,它是所属文件的一个数据元素,而它相对于所含的数据项而言又可看做数据。
因此,在本教材中,对数据和数据元素这两个术语的使用并不加以严格区分。
数据记录(datarecord)简称记录,它是数据处理领域组织数据的基本单位,数据中的每个数据元素在许多应用场合被组织成记录的结构。
一个数据记录由一个或多个数据项(item)所组成,每个数据项可以是简单数据项(即不可再分,如一个数值、一个字符等),也可以是组合数据项(即数组或记录等)。
如对于一个图书目录登记表,它由若干个记录所组成,每个记录都由登录号、书号、书名、作者、出版社、定价等数据项所组成。
在一个表或文件中,若所有记录的某个数据项的值都不同,也就是说,每个值能够惟一地标识一个记录时,则可把这个数据项作为该表或文件的关键数据项,简称关键项(keyitem),关键项中的每一个值称为所在记录的关键字(keyword或key)。
如在图书目录登记表中,登录号数据项的值是互不相同的,所以可把登录号作为图书目录登记表的关键项,其中的每一个值(即每本书的登录号)就是该表的关键字。
在一个表或文件中,能作为关键项的数据项可能没有,可能只有一个,也可能多于一个。
当没有时,可把多个有关的数据项联合起来,构成一个组合关键项,用组合关键项中的每一个组合值来惟一地标识一个记录,该组合值就是所在记录的关键字。
如在旅店管理中,可把楼号和房间号联合起来作为关键字,用它来惟一标识一个房间。
引入了记录的关键项和关键字后,为简便起见,在以后的讨论中,经常利用关键项来代替所有记录,利用关键字来代替所对应的记录。
数据处理(dataprocessing)是指对数据进行查找、插入、删除、合并、拆分、排序、统计、计算、转换、输入、输出、传送等的操作过程。
在早期,计算机主要用于科学和工程计算,而现在则主要用于数据处理。
像计算机情报检索系统、网上订票系统、图书管理系统、物资调配系统、银行存取款系统、财务管理系统等都是计算机在数据处理领域的具体应用。
数据结构(datastructure)是指数据以及相互之间的联系。
上面提到,数据的描述对象是现实世界的事物及其活动,而任何事物及其活动都不是孤立存在的,都是在一定意义上相互联系、相互影响的,所以数据之间必然存在着联系。
数据之间的相互联系,被称为数据的逻辑结构。
在计算机中存储数据时,不仅要存储数据本身,而且要存储它们之间的联系(即逻辑结构)。
一种数据结构在存储器中的存储方式称为数据的物理结构或存储结构。
由于存储方式有顺序、链接、索引、散列等多种形式,所以,一种数据结构可以根据应用的需要表示成任一种或几种组合的存储结构。
数据的逻辑结构和存储结构都反映数据的结构,但通常所说的数据结构是指数据的逻辑结构,不包含存储结构的含义。
为了更确切地描述一种数据结构,通常采用二元组表示:
B=(K,R)
B代表一种数据结构,它由数据元素的集合K和K上二元关系的集合R所组成。
其中
K={ki|1in,n0}
R={rj|1jm,m0}
其中ki表示集合K中的第i个数据元素,n为K中数据元素的个数,特殊地,若n=0,则K是一个空集,此时B也就无结构而言,有时也可以认为它具有任一结构。
rj表示集合R中的第j个二元关系(以后均简称关系),m为R中关系的个数,特别地,若m=0,则R是一个空集,表明不考虑集合K中的元素之间存在任何关系,彼此是独立的,就像数学中集合里的元素一样。
在本书所讨论的数据结构中,一般只讨论m=1的情况,即R中只包含一个关系(R={r})的情况。
对于包含有多个关系的数据结构,可分别对每一个关系进行讨论。
K上的一个关系r(以后直接用大写R表示)是序偶的集合。
对于R中的任一序偶(x,y∈K),我们把x叫做序偶的第一元素,把y叫做序偶的第二元素,又称序偶的第一元素为第二元素的直接前驱(通常简称前驱),称第二元素为第一元素的直接后继(通常简称后继)。
如在的序偶中,x为y的前驱,而y为x的后继。
一种数据结构还能够利用图形形象地表示出来,图形中的每个结点(或叫顶点)对应着一个数据元素,两结点之间带箭头的连线(称做有向边或弧)对应着关系中的一个序偶,其中序偶的第一元素为有向边的起始结点,第二元素为有向边的终止结点,即箭头所指向的结点。
作为例子,下面根据表1-1构造出一些典型的数据结构。
表1-1某个公司人事简表
职工号
姓名
性别
出生日期
职务
部门
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中共有10条记录,每条记录都由六个数据项所组成。
由于每条记录的职工号各不相同,所以可把每条记录的职工号作为该记录的关键字。
在下面的例子中,我们将用记录的关键字来代表整个记录。
例1-1一种数据结构set=(K,R),其中
K={01,02,03,04,05,06,07,08,09,10}
R={}
在数据结构set中,只存在元素的集合,不存在关系的集合,或者说关系为空,这表明我们只考虑表1-1中的每条记录,并不考虑它们之间的任何联系。
具有此种特点的数据结构被称为集合结构。
对于集合结构,也可以看做元素按任一次序排列的线性结构,在存储空间中可以根据需要按任一种存储方式进行存储。
例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),其中
K={01,02,03,04,05,06,07,08,09,10}
R={<01,02>,<01,03>,<01,04>,<02,05>,<02,06>,<03,07>,
<03,08>,<03,09>,<04,10>}
对应的图形如图1-2所示。
图1-2数据的树形结构示意图
结合表1-1,细心的读者不难看出:
R是人员之间领导与被领导的关系。
图1-2像倒着画的一棵树,在这棵树中,最上面的一个没有前驱只有后继的结点叫做树根结点,最下面一层的只有前驱没有后继的结点叫做树叶结点,除树根和树叶之外的结点叫做树枝结点(实际上,树根结点是一种特殊的树枝结点)。
在一棵树中,每个结点有且只有一个前驱结点(树根结点除外),但可以有任意多个后继结点(树叶结点可看做具有0个后继结点)。
这种数据结构的特点是数据元素之间的1对N(1∶N,N0联系),即层次关系,我们把具有这种特点的数据结构叫做树形结构,简称树。
例1-4一种数据结构graph=(K,R),其中
K={01,02,03,04,05,06,07}
R={<01,02>,<02,01>,<01,04>,<04,01>,<02,03>,<03,02>,<02,06>,<06,02>,
<02,07>,<07,02>,<03,07>,<07,03>,<04,06>,<06,04>,<05,07>,<07,05>}
对应的图形如图1-3所示。
从图1-3可以看出,R是K上的对称关系。
为了简化起见,我们把和这两个对称序偶用一个无序对(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所示。
图1-3数据的图形结构示意图图1-4图1-3的等价表示
如果说R中每个序偶里的两个元素所代表的人员是好友的话,那么r关系就是人员之间的好友关系。
从图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={,,,,}
r2={,,,,}
若用实线表示关系r1,虚线表示关系r2,则对应的图形如图1-5所示。
图1-5带有两个关系的数据结构示意图
从图1-5可以看出:
数据结构B是一种非线性的图形结构。
但是,若只考虑关系r1则为树形结构,若只考虑关系r2则为线性结构。
数据类型(datatype)是对数据的取值范围、数据元素之间的结构以及允许施加操作的一种综合描述。
每一种计算机高级语言都定义有自己的数据类型。
在通用的计算机高级语言中,一般都具有整数、实数(浮点数)、枚举、字符、字符串、指针、数组、记录、文件等数据类型。
如整数类型在计算机系统中通常用两个字节或四个字节表示,若采用两个字节,则整数表示范围在–215215–1,即–3276832767之间;若采用四个字节,则整数表示范围在–231231–1,即–21474836482147483647之间。
对整数类型的数据允许施加的操作通常有:
单目取正或取负运算,双目加、减、乘、除、取模等运算,双目等于、不等于、大于、大于等于、小于、小于等于等关系(比较)运算以及赋值运算。
字符类型在机器中通常用一个字节表示,无符号表示范围在0255之间,能够对至多256种字符进行编码。
对字符类型的数据允许进行的操作主要为赋值和各种关系运算。
字符串类型为字符类型的顺序排列结构,每一个字符序列(其最大长度由具体语言规定)都是字符串类型中的一个值。
对字符串的操作主要有求串长度、串拷贝、两串连接、两串比较等。
数据类型可分为简单类型和结构类型两种。
简单类型中的每个数据(即简单数据)都是无法再分割的整体,如一个整数、实数、字符、指针、枚举量等都是无法再分割的整体,所以它们所属的类型均为简单类型。
结构类型由简单类型按照一定的规则构造而成,并且结构类型仍可以包含结构类型,所以一种结构类型中的数据(即结构数据)可以分解为若干个简单数据或结构数据,每个结构数据仍可再分。
如数组就是一种结构类型,它由固定个数的同一类型顺序排列而成,数组类型中的每一个数组值包含有固定个数的同一类型数据,每个数据(元素)都可以通过下标运算符直接访问。
记录也是一种结构类型,它由固定个数的不同(也可以相同)类型顺序排列而成,记录类型中的每一个记录值包含有固定个数的不同类型数据,每个数据都可以通过成员运算符直接访问。
另外,字符串和文件也都是结构类型。
无论是简单类型还是结构类型都有“型”和“值”的区别,“型”是“值”的抽象,一种数据类型中的任一数据称为该类型中的一个值(又称为实例),该值(实例)与所属数据类型具有完全相同的结构,数据类型所规定的操作就是在值上进行的。
在以后的讨论中,一般并不严格区分数据的“型”和“值”的概念,读者应根据上下文叙述加以理解。
如提到记录时,当讨论的是记录结构则认为是记录型,当讨论的是具体的一条记录则认为是记录值。
对于在计算机语言中使用的数组、记录、字符串和文件等结构类型,每个值中的元素可看做是按位置有序排列的,就此均认为它们具有线性结构。
数组的数据结构可描述如下:
array=(A,R),其中
A={a[i]|0in–1,n1}
R={|0in–2}
a[i]为数组中的下标为i的元素,n为大于等于1的整数,用来表明数组中元素的个数,数组元素的下标从0到n–1。
数组中前后相邻位置上的两个元素为一个序偶,其前一元素a[i]是后一元素a[i+1]的前驱,而a[i+1]是a[i]的后继,第一个元素a[0]无前驱元素,最后一个元素a[n–1]无后继元素。
按数组下标的个数,可把数组分为一维、二维、三维等等。
一维数组中的每个元素只包含有一个下标,二维数组中的每个元素包含有两个下标,第一个称为行下标,第二个称为列下标。
二维数组可看做是一维数组的推广或嵌套,即首先把它看做是按行下标顺序排列的一维数组,该数组中的每个元素又都是按列下标顺序排列的一维数组。
如对于一个二维数组b[m][n],可看做一维数组b[m],所含元素依次为b[0],b[1],…,b[m–1],其中每一个元素b[i](0im–1)又都是一个含有n个元素的一维数组,所含元素依次为b[i][0],b[i][1],…,b[i][n–1]。
同样,三维数组包含有三个下标,每个元素的位置由一组三个下标值惟一确定。
三维数组是一维数组的三层嵌套结构。
如对于一个三维数组c[p][m][n],首先可看做是一维数组c[p],所含元素依次为c[0],c[1],…,c[p–1],其中每一个元素c[k](0kp–1)又都是一个含有m个元素的一维数组,所含元素依次为c[k][0],c[k][1],…,c[k][m–1],这里的每一个元素c[k][i](0im–1)也都是一个含有n个元素的一维数组,所含元素依次为c[k][i][0],c[k][i][1],…,c[k][i][n–1]。
数组的存储结构是顺序结构,即数组中第i+1个元素紧接着存储在第i个元素的存储位置的后面。
如对于一维数组a[n],则每个元素a[i]的存储位置的首字节地址为:
LOC(a[i])=LOC(a[0])+i·L(0in–1)
其中LOC(a[0]) 表示数组a中第一个元素的存储位置,它可以直接用数组名a来表示,因为单独使用数组名即为该数组首地址的符号常量;L表示数组a中元素类型的大小,即每个元素所占用的字节数,可用sizeof(a[i]) 计算得到。
由上述公式可知:
元素a[0]的存储地址为a,a[1]的存储地址为a+1L,a[2]的存储地址为a+2L,…,a[n–1]的存储地址为a+(n–1)·L。
假定一维数组a[n]为int类型,并假定int类型的大小为4个字节,则该数组中任一元素a[i]的存储地址为LOC(a[0])+4i。
对于一个二维数组b[m][n],每一行元素b[i]的存储位置(即存储该行n个元素的首字节地址)为:
LOC(b[i])=LOC(b[0][0])+i·RS(0im–1)
其中LOC(b[0][0]) 表示整个数组中第一个元素的存储地址,亦即整个数组的首地址,它实际上是该数组名b的值,因为单独使用任何数组名都代表该数组的首地址常量;RS表示顺序存储一行n个元素所占用存储空间的大小,它等于每个元素所占用的字节数L与一行上元素的个数n的乘积。
因此上述计算公式可改写为:
LOC(b[i])=(char*)b+i·n·L(0im–1)
对于二维数组b中的下标为i的行,其中下标为j的元素b[i][j]的存储位置为:
LOC(b[i][j])=(char*)b+i·n·L+j·L(0im–1,0jn–1)
对于三维或更高维数组,其每个元素的存储位置的首字节地址也容易计算出来。
如对于三维数组c[p][m][n],其相应的一维数组元素、二维数组元素和三维数组元素的存储位置(即首字节地址)的计算公式分别如下:
LOC(c[k])=(char*)c+k·m·n·L(0kp–1)
LOC(c[k][i])=(char*)c+k·m·n·L+i·n·L(0kp–1,0im–1)
LOC(c[k][i][j])=(char*)c+k·m·n·L+i·n·L+j·L(0kp–1,0im–1,0jn–1)
数据对象(dataobject)简称对象,它属于一种数据类型中的具体值,可以用常量或变量表示出来。
如25为一个整型数据对象,'A'为一个字符数据对象,语句char*p定义p为一个字符指针对象,inta[10]定义a为一个含有10个整型数的数组对象,structrectangler1定义r1为一个结构类型的对象。
算法(algorithm)就是解决特定问题的方法。
描述一个算法可以采用文字叙述,也可以采用传统流程图、N-S图或PAD图等,但要在计算机上实现,则最终必须采用一种程序设计语言进行描述,即编写为程序。
作为一个算法应具备以下5个特性。
有穷性:
一个算法必须在执行有穷步之后结束。
确定性:
算法中的每一步都必须具有确切的含义,无二义性。
可行性:
算法中的每一步都必须是可行的,也就是说,每一步都能够通过手工或机器可以接受的有限次操作在有限时间内实现。
输入:
一个算法可以有0个、1个或多个输入量,在算法被执行之前提供给算法。
输出:
一个算法执行结束后至少要有一个输出量,它是利用算法对输入量进行运算和处理的结果。
通常,需要人们解决的特定问题可被分为数值的和非数值的两大类。
解决数值问题的算法叫做数值算法。
科学和工程计算方面的算法都属于数值算法,如求解数值积分,求解线性方程组,求解代数方程,求解微分方程等。
解决非数值问题的算法叫做非数值算法。
数据处理方面的算法都属于非数值算法,如在各种数据结构上进行的排序算法、查找算法、插入算法、删除算法、遍历算法等。
数值算法和非数值算法并没有严格的划分,一般说来,在数值算法中主要进行算术运算,而在非数值算法中,则主要进行比较和逻辑运算。
另一方面,特定的问题可能是递归的,也可能是非递归的,因而解决它们的算法就有递归算法和非递归算法之分。
当然,从理论上讲,任何递归算法都可以通过循环、堆栈等程序设计技术转化为非递归算法。
在计算机领域,一个算法实质上是针对所处理问题的需要,在数据的逻辑结构和存储结构的基础上施加的一种运算。
由于数据的逻辑结构和存储结构不是惟一的,在很大程度上可以由用户根据需要自行选择和设计,所以处理同一个问题的算法也不是惟一的。
另外,即使对于具有相同的逻辑结构和存储结构,其算法的设计思想和技巧不同,编写出的算法也大不相同。
我们学习数据结构这门课程的目的,就是要学会根据数据处理问题的需要,为待处理的数据选择合适的逻辑结构和存储结构,进而按照结构化、模块化以及面向过程或对象的程序设计方法设计出比较满意的算法。
1.2算法描述
图1-6求n个元素中的最大值
算法就是解决特定问题的方法,该方法可以借助各种工具描述出来。
如从n个整数元素中查找出最大值,若用流程图描述则如图1-6所示。
若采用文字描述,则如下列步骤所示:
(1)给n个元素a1an输入数值;
(2)把第一个元素a1赋给用于保存最大值元素的变量x;
(3)把表示下标的变量i赋初值2;
(4)如果i<=n则向下执行,否则输出最大值x后结束算法;
(5)如果ai>x则将ai赋给x,否则不改变x的值,这使得x始终保存着当前比较过的所有元素的最大值;
(6)使下标i增1,以指示下一个元素;
(7)转向第(4)步继续执行。
若要使一个算法在计算机上实现,则最终必须采用一种程序设计语言进行描述。
如对于上述算法,采用C语言描述如下:
/*程序1-1.c*/
#include
#defineN8/*定义N常量为整数8*/
voidmain(void)
{
inti,x,a[N];/*用a[0]~a[N–1]保存a1~an元素*/
printf("请输入%d个整数:
",N);
for(i=0;iscanf("%d",&a[i]);
x=a[0];/*把第一个元素a[0]的值赋给x*/
i=1;/*把第二个元素a[1]的下标1赋给i*/
while(iif(a[i]>x)x=a[i];
i++;
}
printf("%d个整数中的最大值为:
%d\n",N,x);
}
本书对所有算法一般采用文字和C语言两种描述,文字描述给出算法的思路和执行步骤,C语言描述给出在机器上实现的代码。
1.3算法评价
对于解决同一个问题,往往能够编写出许多不同的算法。
例如,对于数据排序问题,就有多种不同的排序算法。
进行算法评价的目的,既在于从解决同一问题的不同算法中选择出较为合适的一种,又在于知道如何对现有算法进行改进,从而有可能设计出更好的算法。
一般从六个方面对算法进行评价。
1.正确性
正确性(correctness)是设计和评价一个算法的