第3章表结构 3.1数 据 结 构 3.1.1数据 数据就是指能够被计算机识别、存储和加工处理的信息的载体。数据元素是数据的基本单位,有时一个数据元素可以由若干个数据项组成。数据项是具有独立含义的最小标识单位。如整数集合中,10这个数就可称为一个数据元素。又如在一个数据库(关系数据库)中,一个记录可称为一个数据元素,而这个元素中的某一字段就是一个数据项。 3.1.2数据类型 数据类型是一个值的集合以及在这些值上定义的一组操作的总称。 同一类数据的全体称为一个数据类型。在程序设计高级语言中,数据类型用来说明一个数据在数据分类中的归属。它是数据的一种属性。这个属性限定了该数据的变化范围。为了解题的需要,根据数据结构的种类,高级语言定义了一系列数据类型。不同的高级语言所定义的数据类型不尽相同。C++语言所定义的数据类型的种类如图31所示。 图31C++语言所定义的数据类型 其中,基本数据类型对应于简单的数据结构,非基本数据类型对应于复杂的数据结构。在复杂的数据结构中,允许成分数据本身具有复杂的数据结构,因而,非基本数据类型允许复合嵌套。指针类型对应于数据结构中成分数据之间的关系,表面上属基本数据类型,实际上都指向复杂的成分数据,即构造数据类型中的数据,因此这里没有把它划入基本数据类型,而是把它划入非基本数据类型。 3.1.3数据结构的定义 数据结构是在整个计算机科学与技术领域中被广泛使用的术语。它被用来反映一个数据的内部构成,即一个数据由哪些成分数据构成、以什么方式构成、呈什么结构。数据结构有逻辑上的数据结构和物理上的数据结构之分。逻辑上的数据结构反映成分数据之间的逻辑关系,物理上的数据结构反映成分数据在计算机内的存储安排。 数据结构是相互之间存在着一种或多种特定关系的数据元素的集合。数据结构的定义虽然没有标准,但是它包括逻辑结构、存储结构和数据操作三方面内容。 1. 逻辑结构 表31所示是一个班级学生成绩表,包括学生的学号、姓名、语文、数学、物理成绩等。这些形成了一个数据结构,它由很多记录(数据元素)组成,每个元素又由多个数据列(字段、数据项)组成。每个学生基本信息记录对应一个数据元素,学生记录按顺序号排列,形成了学生基本信息记录的线性序列。分析数据结构都是从结点(其实也就是元素、记录、顶点,虽然在各种情况下所用名字不同,但说的是同一个东西)之间的关系来分析的,对于这个表中的任一个记录(结点),它只有一个直接前趋,只有一个直接后继(前趋、后继就是前相邻、后相邻的意思),整个表只有一个开始结点和一个终端结点。知道了这些关系就能明白这个表的逻辑结构,即逻辑结构就是数据元素之间的逻辑关系。 表31学生成绩表 学号姓名语文数学物理 021001王强879096 021002李一龙699189 021003张映月877971 021004何一端848868 …………… 2. 存储结构 存储结构是指用计算机语言如何表示结点之间的这种关系,即数据的逻辑结构用计算机语言的实现。常用的数据存储结构有顺序存储方法、链式存储方法、索引存储方法和散列存储方法四种。 1) 顺序存储方法 例如将多个机器人分配到各个工位上,按工位号顺序分配机器人,机器人a1在一号工位、机器人a2在二号工位……。这种方法把逻辑上相邻的结点存储在物理位置上相邻的存储单元中,结点间的逻辑关系由存储单元的邻接关系来体现,如图32所示。由此得到的存储表示称为顺序存储结构(sequential storage structure),通常借助程序语言的数组描述。 图32顺序存储 顺序存储的优点是随机访问快,可以直接通过数据的地址访问; 缺点是插入、删除效率低,不利于动态增长。 该方法主要应用于线性的数据结构。非线性的数据结构也可通过某种线性化的方法实现顺序存储。 2) 链式存储方法 假设将多个机器人分配到各个工位上,机器人a1在一号工位同时知道机器人a2所在的工位号,机器人a2知道机器人a3的工位号……。这种分配方式,机器人的工位号不连续,只有找到第一个机器人才能找到后续一个机器人的工位号。该方法不要求逻辑上相邻的结点在物理位置上也相邻,结点间的逻辑关系由附加的指针字段表示,如图33所示。由此得到的存储表示称为链式存储结构(linked storage structure),通常借助于程序语言的指针类型描述。 图33链式存储 链式存储的优点是便于插入、删除和动态增长; 缺点是不能直接得到相关数据的地址,随机访问慢,空间开销大,占用空间较多。 3) 索引存储方法 该方法通常在存储结点信息的同时,还建立附加的索引表。索引表由若干索引项组成,索引项的一般形式是: (关键字、地址) 关键字是能唯一标识一个结点的那些数据项。 若每个结点在索引表中都有一个索引项,则该索引表称为稠密索引(dense index)。若一组结点在索引表中只对应一个索引项,则该索引表称为稀疏索引(spare index)。稠密索引中索引项的地址指示结点所在的存储位置; 稀疏索引中索引项的地址指示一组结点的起始存储位置。 例如,对于下列结构City,将区号看成是关键字,其索引存储结构如图34所示。索引表由(区号,地址)组成,其中区号按递增次序排序。 图34City的索引存储结构 索引存储结构采用顺序和链式结合的方式,数据检索速度快,能够保证数据的唯一性。但这种存储结构创建索引和维护索引需要时间,索引也会占用一定的物理空间,对数据增、删、查、改的同时也要对索引进行维护。 4) 散列存储方法 例如有一组数据dat={1023, 1074, 1077},通过分析发现,各个元素后两位是变化的,其他两位数不变。那么地址取值可以是23、74、77。 这种存储方法是一种在数据元素的存储位置与关键字之间建立确定对应关系的存储技术,又称Hash存储。该方法的基本思想是: 根据结点的关键字通过一定的函数关系计算出该结点的存储地址。上面的例子中,能够对1023、1074、1077进行处理得到地址23、74、77的函数即为散列函数,又称为哈希(Hash函数)。这种存储结构利用数据的某一特征访问和存储,访问速度快; 缺点是好的散列很难,有时会产生冲突。 上述四种基本存储方法,既可单独使用,也可组合起来对数据结构进行存储映像。同一逻辑结构采用不同的存储方法,可以得到不同的存储结构。选择何种存储结构来表示相应的逻辑结构,视具体要求而定,主要考虑运算方便及算法的时空要求。 3. 数据操作 如一张表格,需要进行查找、增加、修改、删除、排序等工作,这就是数据的运算,它不仅仅是加、减、乘、除这些算术运算,在数据结构中,这些运算常常涉及算法问题。 数据结构反映数据内部的构成方式,它常常用一个结构图来描述: 数据中的每一项成分数据被看作一个结点,并用方框或圆圈表示,成分数据之间的关系用相应的结点之间带箭头的连线表示。如果成分数据本身又有它自身的结构,则结构出现嵌套。这里嵌套还允许是递归的嵌套。 3.1.4数据结构的分类 通常将数据的逻辑结构简称为数据结构。按数据结构中成分数据之间的关系,数据结构分为线性结构和非线性结构两大类。 1. 线性结构 如果结构为非空集,则该结构有且只有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前趋和一个直接后继。线性表就是一个典型的线性结构。 2. 非线性结构 非线性结构的逻辑特征是该结构中一个数据元素可能有多个直接前趋和直接后继。非线性结构中最普遍的就是图的结构。在非线性数据结构中又有层次与网状之分。 3.2线性表 线性表(linear list)是一种最简单、最常用的数据结构。线性表的存储结构通常分为顺序存储结构和链式存储结构。前者是用顺序存储结构存放的线性表,称为顺序表; 后者是用链式结构存储的线性表,称为线性链表。 3.2.1线性表的定义和运算 1. 线性表的定义 线性表是一组类型相同的数据元素a0,a1,…,an-1的有限序列,记为(a0,a1,…,an-1)。在线性表中,数据元素的个数n定义为线性表的长度,n=0的表称为空表。 当表的长度n≥1时,a0是表的第一个元素,an-1是最后一个元素。除a0外,表中每一个数据元素ai(1≤i≤n-1)只有一个直接前趋(predecessor)ai-1,除ai-1外,表中每个数据元素ai(1≤i≤n-1)仅有一个直接后继(successor)ai+1。数据元素在表中的位置只取决于它自身的序号,数据元素间的相对位置是线性的,因此称线性表是一种线性结构。 例如,26个大写英文字母组成的字母表(A,B,C,D,…,Z)是一个线性表。其中,A是第一个数据元素,Z是最后一个数据元素,A是B的直接前趋,B是A的直接后继,线性表的长度是26。 线性表中的数据元素也称为结点。它可以是一个整数、一个实数、一个字符或一个字符串; 也可以由若干个数据项(item)组成,其中每个数据项可以是一般数据类型,也可以是构造类型。 表32的线性表用于记录最近一周每天的平均气温。每个结点有两个数据项: 一个是星期,它的数据类型是由三个字符组成的字符串; 另一个是温度,它的数据类型是实型数据。一般称多个数据项组成的数据元素为记录,称数据元素为记录的线性表为文件(file)。 表32一周内每天的平均气温记录表 MonTueWedThuFriSatSun 15.516.015.715.016.116.416.5 2. 线性表的运算 线性表的基本运算是插入、删除、查找、排序等。插入是指在表的两个确定的元素之间插入一个新的数据元素; 删除是指删掉表中某个数据元素; 查找是指查询表中满足某种条件的数据元素; 排序是指根据结点的某个字段值按升序(或降序)重新排列线性表。可以将几个线性表合并成一个线性表,或把一个线性表拆成几个线性表,求线性表的长度等。其中,查找、插入、删除是线性表常见的三种基本运算。 3.2.2顺序存储的线性表 1. 顺序表 顺序表(sequential list)是用一组连续的存储单元依次存放等长数据元素的线性表,也称为线性表的顺序存储结构。这组连续的存储单元称为向量(vector)。在计算机中顺序存储结构是表示线性表的最简单方法。 1) 顺序表的存储地址 图35顺序表示意图 由于顺序表中所有结点的数据类型是相同的,因此每个结点在存储器中占用大小相同的空间。若一个数据元素仅占一个存储单元, 则这种存储方式如图35所示。由图35可见,顺序表第i个数据元素的存储地址为L(ai)=L(a0)+i,其中L(a0)是线性表第一个数据元素的存储地址。 若每个数据元素都占用k个存储单元,并以L(a0)为第一个数据元素存储单元地址(顺序表的首地址),则第i个数据元素的存储位置为L(ai)=L(a0)+i×k。 2) 顺序表的特点 顺序表的特点是表中逻辑上相邻的数据元素存储在相邻的存储位置。换句话说,以数据元素在计算机内“物理位置相邻”来表示表中的数据元素间的逻辑关系。对于这种存储方式,访问第i个数据元素,就可以直接计算出ai的存储地址L(ai),因而能随机存取表中任一数据元素。换言之,数据元素在顺序表中的存储位置取决于该数据元素在顺序表中的顺序号。 顺序表可用C语言的一维数组实现。数组的类型随着数据元素的性质而定。描述方法为: # define M 1000; int a[M]; 它表示数组名为a,该数组有M个数据元素(M=1000),下标从0开始。设线性表为(a0,a1,…,an-1),nn )){ return(-1); }else{ for(j=n; j>=pos-1;j--){ a[j+1]=a[j]; //将插入点后的数据逐个向后移动 showlist(a,n+1); } a[pos-1] = x; return(0); } } 调用代码如下: void main(){ int pos, data, retflag; static int a[7] = {1, 2, 5, 2, 3, 5}; //插入操作数组中至少留一个空位 int n=7; //数组长度,数组元素个数 int n1=6; //当前有效数据个数 cout<<"当前数组: "<n)){ return(-1); }else{ for(j=PosArr;j