8.2 数组、矩阵和广义表
数组与广义表可看作是线性表的推广,其特点是数据元素仍然是一个表。
这里讨论多维数组的逻辑就结构和存储结构,特殊矩阵和矩阵的压缩存储,广义表的逻辑结构、存储结构和基本运算。
8.2.1 数组
1、数组的定义及基本运算
1)数组的定义
数组是定义长线性表在维数上的扩张,即线性表中的元素又是一个线性表。
n 维数组是一种 “ 同构 ”的数据结构,其每个数据元素类型相同,结构一致。
设有 n 维数组 A[b1,b2,...,bn],其每一维的下界都有 1,bi 是第 i 维的上界。
从数据结构的逻辑关系角度来看, A中的每个元素 A[j1,j2,...,jn](1≤ji≤bi)都被 n 个关系所约束。在每个关系中,除第一个和最后一个元素外,其余元素都只有一个直接后继和一个直接前驱。因此就单个关系而言,仍是线性的。
以二维数组 A[m,n]为例,可以把它看成是一个定长的线性表,它的每个元素也是一个定长线性表。
A 可看成一个行向量形式的线性表:
或列向量形式的线性表:
数组结构的特点如下。
(1)数据元素数目固定。一旦定义了一个数组结构,就不再有元素个数的增减变化。
(2)数据元素具有形同的类型。
(3)数据元素的下标关系具有上下界的约束且下标有序。
2)数组的两个基本运算
(1)给定一个组下标,存取相应的数据元素。
(2)给定一个组下标,修改相应的数据元素中某个数据项的值。
几乎所有的高级程序设计语言都提供了数组类型。实际上,在程序语言中把数组看成是具有共同名字的同一类型的多个变量的集合。
2、数组的顺序存储
数组一般不作插入也删除运算,一旦定义了数组,则结构中的数据元素个数和元素之间的关系就不再发生变动,因此数组适合于采用顺序存储结构。
由于计算机的内存结构是一维线性的,因此存储多维数组时必须按某种方式进行降维处理,即将数组元素排成一个线性序列,这就产生了次序约定问题。
因为多维数组是由较低一维的数组来定义的,以此类推,通过这种递推关系将多维数组的数据元素排成一个线性序列。
对于数组,一旦确定了其维数和各维的长度,便可为它分配存储空间。
反之,只要给出一组下标便可求得相应数组元素的存储位置,即在数据的顺序存储结构中,数据元素的位置是其下标的线性函数。
二维数组的存储结构可分为以行为为主序和以列为主序的两种方法,如图 8-11 所示,
设每个数据元素占用 L 个单元,m、n为数组的行数和列数,Loc(a11)表示元素 a11的地址,那么以行为主序优先存储的地址计算公式为
同理,以列为主序优先存储的地址计算公式为
推广至多维数组,按下表顺序存储时,先排最右的下标,从右向左直到最左下标,而逆下标顺序则正好相反。
8.2.2 矩阵
矩阵是很多科学与工程计算问题中研究的数学对象。
在数据结构中,主要讨论如何在节省存储空间的情况下,使矩阵的各种运算能高效地进行。
在一些矩阵中,存在很多值相同的元素或者是 0 元素。为了节省存储空间,可以对这类矩阵进行压缩存储,即为多个值相同的元素只分配一个存储单元,对 0 元不分配存储单元。假如值相同的元素或 0 元在矩阵中的分布有一定的规律,则称此类矩阵为特殊矩阵,否则称为稀疏矩阵。
1、特殊矩阵
若矩阵中元素(或非 0 元素)的分布有一定的规律,则称之为特殊矩阵。
常见的特殊矩阵有对称矩阵、三角矩阵和对角矩阵等,由于其非 0 元的分布有一定的规律,所以可将其压缩存储在一维数组中,并建立起每个非 0 元在矩阵中的位置与其在一维数组中的位置之间的对应关系。
若矩阵 An×m 中的元素特点为 aij = aji(1≤i,j≤n),则称之为 n 阶对称矩阵。
若对称矩阵中的每一对元素仅占用一个存储单元,那么就可将 个元素压缩存储到能存放n(n+1)/2个元素的存储空间中。不失一般性,以行为主序存储下三角(包括对角线)中的元素。
假设以一维数组 B[n(n+1)/2]作为 n 阶对称矩阵 A 的压缩存储空间,则B[k](1≤k≤n(n+1)/2)与矩阵元素aij之间存在着一一对应的关系。
对角矩阵是指矩阵中的非 0 元素都集中在以主对角线为中心的带状区域中,即除了主对角线上合直接在对角线上、下方若干条对角线上的元素外,其余的矩阵元素都是 0。一个 n 阶的三对角矩阵如图 8-12 所示。
若以行为主序将 n阶对角矩阵 An×n的非 0 元素存储在一维数组 B[k](1≤k≤3×n-2)中,则元素位置之间的对应关系为
其他特殊矩阵可作类似的推算,这里不再一一说明。
2、稀疏矩阵
在一个矩阵中,若非 0 元素的个数远远少于0 元素的个数,且非 0 元素分布没有规律,则称之为稀疏矩阵。
对于稀疏矩阵,存储非 0 元素时必须同时存储其位置(即行号和列号),所以三元组(i,j,aij)可唯一确定矩阵 A中的一个元素。由此,一个稀疏矩阵可由表示非 0 元素的三元组及其行、列数唯一确定。
图 8-13 所示的是一个 6行7列的稀疏矩阵,其三元组表为
稀疏矩阵的三元组表的顺序存储结构成为三元组顺序表,常用的三元组表的链式存储结构是十字链表。
8.2.3 广义表
1、广义表的定义
广义表是线性表的推广,是由 0 个或多个单元素或字表组成的有限序列。
广义表与线性表的区别在于:线性表的元素都是在结构上不可分的单元素,而广义表的元素既可以是单元素,也可以是有结构的表。
广义表一般记为:
其中,ai(1≤i≤n)既可以是单个元素,又可以是广义表,分别成为原子和子表。
广义表的长度是指广义表中的个数。广义表的深度是指广义表展开后所含的括号的最大层。
2、广义表的基本操作
与线性表类似,广义表也有查找、插入和删除等操作。由于广义表的结构比较复杂,其各种运算的实现也不如线性表简单,这里只讨论两个重要的运算。
(1)取表头 head(LS)。
非空广义表 LS 的第一个元素称为表头,它可以是一个单元素,也可以是一个子表。
(2)取表尾 tail(LS)。
在非空广义表中,除表头元素之外,由其余元素所构成的表称为表尾。非空广义表的表尾必定是一个表。
3、广义表的特点
(1)广义表可以是多层次的结构,因为广义表的元素可以是子表,而表的元素还可以是子表。
(2)广义表中的元素可以是已经定义的广义表的名字,所以一个广义表可被其他广义表所共享。
(3)广义表可以是一个递归的表,即广义表中的元素也可以是本广义表的名字。
4、广义表的存储结构
由于广义表中的元素本身又可以具有结构,它是一种带有层次的非线性结构,因此难以用顺序存储结构表示,通常采用链式存储结构。由上面讨论可知,若广义表不空,则可分解为表头和表尾两部分。反之,一对确定的表头和表尾唯一确定一个广义表。针对园子和子表可分别设计不同的节点结构,如图 8-14 所示。对于广义表 LS=(a,(b,c,d)),其链式存储结构如图8-15所示。