500 likes | 600 Views
5.1 数组的定义 5.2 数组的顺序表示和实现 5.3 矩阵的压缩存储 5.3.1 特殊矩阵 5.3.2 稀疏矩阵. 第五章 数组. 数组中各元素具有统一的类型,并且数组元素的下标一般具有固定的上界和下界,因此,数组的处理比其它复杂的结构更为简单。 多维数组是向量的推广 。例如,二维数组: a 00 a 01 … a 0,n-1 a 10 a 11 … a 1,n-1 … … … …
E N D
5.1数组的定义 5.2数组的顺序表示和实现 5.3矩阵的压缩存储 5.3.1 特殊矩阵 5.3.2 稀疏矩阵 第五章 数组
数组中各元素具有统一的类型,并且数组元素的下标一般具有固定的上界和下界,因此,数组的处理比其它复杂的结构更为简单。数组中各元素具有统一的类型,并且数组元素的下标一般具有固定的上界和下界,因此,数组的处理比其它复杂的结构更为简单。 多维数组是向量的推广。例如,二维数组: a00 a01 … a0,n-1 a10 a11 … a1,n-1 … … … … am-1,0 am-1,1 … am-1,n-1 既可以看成是由m个行向量组成的向量(行向量形式),也可以看成是n个列向量组成的向量(列向量形式)。 5.1数组的定义
在C语言中,一个二维数组类型可以定义为其分量类型为一维数组类型的一维数组类型,也就是说,在C语言中,一个二维数组类型可以定义为其分量类型为一维数组类型的一维数组类型,也就是说, typedef ElemType Array2[m][n]; 等价于: typedef ElemType Array1[n]; typedef Array1 Array2[m];
同理,一个n维数组类型可以定义为其数据元素为n-1维数组类型的一维数组类型 。 • 数组一旦被定义,它的维数和维界就不再改变。因此,除了结构的初始化和销毁之外,数组只有存取元素和修改元素值的操作。 • 抽象数据类型数组的定义参见教材p.90。
5.1数组的定义 5.2数组的顺序表示和实现 5.3矩阵的压缩存储 5.3.1 特殊矩阵 5.3.2 稀疏矩阵 第五章 数组
使用一维内存来表示多维数组,就必须按某种次序将数组元素排成一列序列,然后将这个线性序列存放在存储器中。使用一维内存来表示多维数组,就必须按某种次序将数组元素排成一列序列,然后将这个线性序列存放在存储器中。 • 此外,数组一旦建立,结构中的元素个数和元素间的关系就不再发生变化。因此,一般都是采用顺序存储的方法来表示数组。 5.2数组的顺序表示和实现
将数组元素按行排列,第i+1个行向量紧接在第i个行向量后面。将数组元素按行排列,第i+1个行向量紧接在第i个行向量后面。 • 以二维数组为例,按“行序为主序”方式存储的线性序列为: a00,a01,…,a0, n-1 ,a10,a11,…a1,n-1,……,am-1,0,am-1,1,…,am-1,n-1 • 在PASCAL、C语言中,数组就是按行优先顺序存储的。 以行序为主序
将数组元素按列向量排列,第j+1个列向量紧接在第j个列向量之后。将数组元素按列向量排列,第j+1个列向量紧接在第j个列向量之后。 • 前述二维数组的m*n个元素按“列序为主序”方式存储的线性序列为:a00,a10,…,am-1,0,a01,a11,…am-1,1,……,a0,n-1,a1,n-1,…,am-1,n-1 • 在FORTRAN语言中,数组就是按列优先顺序存储的。 以列序为主序
对于数组,一旦规定了它的维数和各维的长度,便可为之分配存储空间。对于数组,一旦规定了它的维数和各维的长度,便可为之分配存储空间。 • 反之,只要给出一组下标便可以求得相应数组元素的存储位置。
例如,二维数组Amn按“行序为主序”方式存储在内存中,假设每个元素占用L个存储单元。元素aij的存储地址应是数组的基地址加上排在aij前面的元素所占用的单元数。例如,二维数组Amn按“行序为主序”方式存储在内存中,假设每个元素占用L个存储单元。元素aij的存储地址应是数组的基地址加上排在aij前面的元素所占用的单元数。 • 因为aij位于第i+1行、第j+1列,前面i行一共有i ×n个元素,第i行上aij前面又有j个元素,故它前面一共有i ×n +j 个元素,因此,aij的地址计算函数为: LOC(aij)=LOC(a00)+(i*n+j)*L
在C语言中,数组各维下标的下界是0,因此在C语言中,二维数组的地址计算公式为: LOC(aij)=LOC(a00)+(i*b2+j)*L 其中b2是数组第2维的长度。 • 同样,三维数组Amnp按“行序为主序”方式存储,其地址计算函数为: LOC(aijk)=LOC(a000)+(i*n*p+j*p+k)*L
推至一般情况, • 其中,cn=L, ci-1= bi* ci,1 < i ≤n。 • 容易看出,数组元素的存储位置是其下标(ji)的线性函数,计算各元素存储位置的时间相等。 • 因此,存取数组中任一元素的时间相等,我们称具有这一特点的存储结构为随机存储结构。
5.1数组的定义 5.2数组的顺序表示和实现 5.3矩阵的压缩存储 5.3.1 特殊矩阵 5.3.2 稀疏矩阵 第五章 数组
在科学与工程计算问题中,矩阵是一种常用的数学对象。在科学与工程计算问题中,矩阵是一种常用的数学对象。 • 在高级语言编制程序时,一种简单而又自然的方法就是将一个矩阵描述为一个二维数组。 • 矩阵在这种存储表示之下,可以对其元素进行随机存取,各种矩阵运算也非常简单,并且存储的密度为1。 5.3矩阵的压缩存储
但是,在矩阵中非零元素呈某种规律分布或者矩阵中出现大量的零元素的情况下,看起来存储密度仍为1,但实际上占用了许多单元去存储重复的非零元素或零元素,这对高阶矩阵会造成极大的浪费。但是,在矩阵中非零元素呈某种规律分布或者矩阵中出现大量的零元素的情况下,看起来存储密度仍为1,但实际上占用了许多单元去存储重复的非零元素或零元素,这对高阶矩阵会造成极大的浪费。 • 为了节省存储空间,我们可以对这类矩阵进行压缩存储:即为多个相同的非零元素只分配一个存储空间;对零元素不分配空间。
所谓特殊矩阵是指非零元素或零元素的分布有一定规律的矩阵。下面我们讨论几种特殊矩阵的压缩存储。所谓特殊矩阵是指非零元素或零元素的分布有一定规律的矩阵。下面我们讨论几种特殊矩阵的压缩存储。 • 对称矩阵 在一个n阶方阵A中,若元素满足下述性质: aij=aji0≤i,j ≤n-1 则称A为对称矩阵。 5.3.1特殊矩阵
对称矩阵中的元素关于主对角线对称,故只要存储矩阵中上三角或下三角中的元素,让每两个对称的元素共享一个存储空间即可。对称矩阵中的元素关于主对角线对称,故只要存储矩阵中上三角或下三角中的元素,让每两个对称的元素共享一个存储空间即可。 • 不失一般性,我们按“行序为主序”方式存储主对角线(包括对角线)以下的元素,其存储形式如图所示: 1 5 1 3 7 a00 5 0 8 0 0 a10 a 11 1 8 9 2 6 a20 a21 a22 3 0 2 5 1 ……………….. 7 0 6 1 3 an-1 0 a n-1 1 a n-1 2 …a n-1 n-1 图 5.1对称矩阵
在这个下三角矩阵中,元素总数为n(n+1)/2。 • 因此,我们可以按“由上自下,由左至右”的次序将这些元素存放在一个向量sa[0..n(n+1)/2-1]中。为了便于访问对称矩阵A中的元素,我们必须在矩阵元aij和sa[k]之间找一个对应关系。 当i≥j 当i<j • 称sa[n(n+1)/2]为n阶对称矩阵A的压缩存储。
三角矩阵 • 所谓下 (上) 三角矩阵是指矩阵的上 (下) 三角(不包括对角线)中的元均为常数或零的n阶矩阵。 • a00 a01 … a 0 n-1 a00 c … c • c a11 … a 1 n-1 a10 a11 … c • ………………….. …………….. • c c … a n-1 n-1 an-1 0 an-1 1 … an-1 n-1 • (a)上三角矩阵 (b)下三角矩阵 • 图5.2 三角矩阵
三角矩阵中的重复元c可共享一个存储空间,其余的元素正好有n(n+1)/2个。三角矩阵中的重复元c可共享一个存储空间,其余的元素正好有n(n+1)/2个。 • 因此,三角矩阵可压缩存储到向量sa[0..n(n+1)/2]中,其中c存放在向量的最后一个分量中。 • 上三角矩阵中,如果按“行序为主序”方式存放上三角矩阵中的元素aij时,sa[k]和aij的对应关系是: i(2n-i+1)/2+j-i 当i≤j n(n+1)/2 当i>j • 类似地,下三角矩阵中,sa[k]和aij的对应关系是: i(i+1)/2+j 当i≥j n(n+1)/2 当i<j k= k=
对角矩阵 对角矩阵中,所有的非零元集中在以主对角线为中心的带状区域中,即除了主对角线和主对角线相邻两侧的若干条对角线上的元素之外,其余元素皆为零。下图给出了一个三对角矩阵: a00 a01 a10 a11 a12 a21 a22 a23 …. ….. …. 图5.3 对角矩阵 an-2 n-3 an-2 n-2 an-2 n-1 an-1 n-2 an-1 n-1 • 对角矩阵可按“行序为主序”方式或以对角线的顺序,将其压缩存储到一个向量中,并且也能找到每个非零元素和向量下标的对应关系。
简单说,设矩阵A中有s个非零元素,若s远远小于矩阵元素的总数(即s≤m×n),则称A为稀疏矩阵。简单说,设矩阵A中有s个非零元素,若s远远小于矩阵元素的总数(即s≤m×n),则称A为稀疏矩阵。 • 更精确地,令 e=s/(m*n),称e为矩阵的稀疏因子。通常认为e ≤ 0.05时称之为稀疏矩阵。 5.3.2稀疏矩阵
在存储稀疏矩阵时,为了节省存储单元,很自然地想到使用压缩存储方法。在存储稀疏矩阵时,为了节省存储单元,很自然地想到使用压缩存储方法。 • 但由于非零元素的分布一般是没有规律的,因此在存储非零元素的同时,还必须同时记下它所在的行和列的位置(i,j)。 • 反之,一个三元组(i, j, aij)唯一确定了矩阵A的一个非零元。因此,稀疏矩阵可由表示非零元的三元组及其行、列数唯一确定。
例如,下列三元组表(注意i,j从1开始) ((1,2,12)(1,3,9),(3,1,- 3),(3,6,14),(4,3,24), (5,2,18),(6,1,15),(6,4,-7)) 加上(6, 7)这一对行、列值便可作为下列矩阵M的另一种描述。而由上述三元组表的不同表示方法可引出稀疏矩阵不同的压缩存储方法。 0 12 9 0 0 0 0 0 0 -3 0 0 15 0 0 0 0 0 0 0 12 0 0 0 18 0 -3 0 0 0 0 14 0 9 0 0 24 0 0 0 0 24 0 0 0 0 0 0 0 0 0 –7 0 18 0 0 0 0 0 0 0 14 0 0 0 15 0 0 –7 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 图5.5 稀疏矩阵M和T T= M=
三元组顺序表 假设以顺序存储结构来表示三元组表,则可得到稀疏矩阵的一种压缩存储方法——三元组顺序表。 #define MAXSIZE 10000 typedefstruct{ int i, j; //非零元的行标和列标 ElemType e; }Triple; typedefstruct{ Triple data[MAXSIZE+1]; //非零元三元组表 int mu,nu,tu; //矩阵的行数、列数和非零元个数 }TSMatrix;
设M为TSMatrix型的结构变量,教材p.97上图5.5中所示的稀疏矩阵的三元组的表示如下: i j e 1 2 12 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 • 下面以矩阵的转置为例,说明在这种压缩存储结构上如何实现矩阵的运算。
一个m×n的矩阵M,它的转置T是一个n×m的矩阵,且a[i][j]=b[j][i],0≤i ≤ m,0 ≤ j ≤ n,即M的行是T的列,M的列是T的行。 • (参见p.98) 将M转置为T,就是将M的三元组表a.data置换为T的三元组表b.data,如果只是简单地交换a.data中i和j的内容,那么得到的b.data将是一个按列优先顺序存储的稀疏矩阵T,要得到按“行序为主序”方式顺序存储的b.data,就必须重新排列三元组的顺序。
i j e i j e 1 2 12 2 1 12 1 3 9 3 1 9 3 1 -3 1 3 -3 3 6 14 6 3 14 4 3 24 3 4 24 5 2 18 2 5 18 6 1 15 1 6 15 6 4 -746 -7 a.data b.data (行序为主序) (列序为主序) 调换i,j
由于M的列是T的行,因此,按a.data的列序转置,所得到的转置矩阵T的三元组表b.data必定是按“行序为主序”方式存放的。由于M的列是T的行,因此,按a.data的列序转置,所得到的转置矩阵T的三元组表b.data必定是按“行序为主序”方式存放的。 • 按这种方法设计的算法,其基本思想是:对M中的每一列 col(0≤col≤n-1),通过从头至尾扫描三元表a.data,找出所有列号等于col的那些三元组,将它们的行号和列号互换后依次放入b.data中,即可得到T的按“行序为主序”方式的压缩存储表示。
i j e 1 2 12 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) i j e 1 3 -3 1 6 15 2 1 12 2 5 18 3 1 9 3 4 24 4 6 -7 6 3 14 b.data (行序为主序)
Status TransposeSMatrix(TSMatrix M, TSMatrix &T) { // 采用三元组表存储表示,求稀疏矩阵M的转置矩阵T T.mu = M.nu; T.nu = M.mu; T.tu = M.tu; if (T.tu) { q = 1; for (col = 1; col <= M.nu; ++col) for (p =1; p<= M.tu; ++p) if (M.data[p].j == col) { T.data[q].i = M.data[p].j; T.data[q].j = M.data[p].i; T.data[q].e = M.data[p].e; ++q; } } return OK; } 算法 5.1
一般矩阵转置算法为 for (col = 1; col <= nu; ++col) for (row = 1; row <= mu; ++row) T[col][row] = M[row][col]; • 三元组顺序表虽然节省了存储空间,但时间复杂度(O(nu*tu))比一般矩阵转置的算法(O(mu*nu))还要复杂,同时还有可能增加算法的难度。因此,此算法仅适用于tu≤mu*nu的情况。
下面给出另外一种称之为快速转置的算法,其算法思想为:按照a.data中三元组的次序进行转置,并将转置后的三元组置入b中恰当位置。下面给出另外一种称之为快速转置的算法,其算法思想为:按照a.data中三元组的次序进行转置,并将转置后的三元组置入b中恰当位置。 • 如果能预先确定矩阵M中每一列的第一个非零元在b.data中应有的位置,那么在对a.data中的三元组依次作转置时,可以直接放到b.data中恰当位置上去。 • 为了预先确定矩阵M中的每一列的第一个非零元素在数组b.data中应有的位置,需要先求得矩阵M中的每一列中非零元素的个数。
因为:矩阵M中第i列的第一个非零元素在数组b.data中应有的位置等于第i-1列第一个非零元素的位置加上第i-1列非零元素的个数。因为:矩阵M中第i列的第一个非零元素在数组b.data中应有的位置等于第i-1列第一个非零元素的位置加上第i-1列非零元素的个数。 • 为此,需要设置两个一维数组num[0..n]和cpot[0..n] • num[0..n]:统计M中每列非零元素的个数,num[col]的值可以由M的第二列(即j列)求得。 • cpot[0..n]:由递推关系得出M中的每列第一个非零元素在T中的位置。
算法通过cpot数组建立位置对应关系: cpot[1]=1 cpot[col]=cpot[col-1]+num[col-1] 2≤col≤a.n 例如:图5.5中的矩阵M和相应的三元组可以求得num[col]和 cpot[col]的值如下: col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 1 3 5 7 8 8 9 算法时间复杂度O(n+t) , 参见教材p.100 算法5.2
i j v 2 1 12 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 (T中第2行第1个非零元) 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 1 3 5 7 8 8 9
i j v 2 1 12 3 1 9 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 1 3 9 (T中第3行第1个非零元) 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 1 45 7 8 8 9
i j v 1 3 -3 2 1 12 3 1 9 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 1 3 9 3 1 -3 (T中第1行第1个非零元) 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 146 7 8 8 9
i j v 1 3 -3 2 1 12 3 1 9 6 3 14 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 1 3 9 3 1 -3 3 6 14 (T中第6行第1个非零元) 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 246 7 8 8 9
i j v 1 3 -3 2 1 12 3 1 9 3 4 24 6 3 14 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 246 7 8 9 9
i j v 1 3 -3 2 1 12 2 5 18 3 1 9 3 4 24 6 3 14 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 247 7 8 9 9
i j v 1 3 -3 1 6 15 2 1 12 2 5 18 3 1 9 3 4 24 6 3 14 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 257 7 8 9 9
i j v 1 3 -3 1 6 15 2 1 12 2 5 18 3 1 9 3 4 24 4 6 -7 6 3 14 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7(T中第4行第1个非零元) a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 3577 8 9 9
i j v 1 3 -3 1 6 15 2 1 12 2 5 18 3 1 9 3 4 24 4 6 -7 6 3 14 b.data (行序为主序) 位置 1 2 3 4 5 6 7 8 位置 1 2 3 4 5 6 7 8 i j v 1 2 12 1 3 9 3 1 -3 3 6 14 4 3 24 5 2 18 6 1 15 6 4 -7 a.data (行序为主序) col 1 2 3 4 5 6 7 num[col] 2 2 2 1 0 1 0 cpot[col] 3578 8 9 9
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T) { // 采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T T.mu = M.nu; T.nu = M.mu; T.tu = M.tu; if (T.tu) { for (col=1; col<=M.nu; ++col) num[col]=0; for (t=1; t<=M.tu; ++t) ++num[M.data[t].j]; //求num[col] cpot[1]=1; //求第col列中第一个非零元素在b.data中的序号 for (col=2; col<=M.nu; ++col) cpot[col] = cpot[col-1]+num[col-1]; for (p=1; p<=M.tu; ++p) { col = M.data[p].j; q=cpot[col]; //求非零元的位置(或序号) T.data[q].i=M.data[p].j; T.data[q].j=M.data[p].i; T.data[q].e=M.data[p].e; ++cpot[col]; //设置该列中下一个非零元的位置 }//for return OK: } //FastTransposeSMatrix 算法 5.2
从时间上看,算法5.2有4个并列的单循环,其时间复杂度为O(nu+tu)。从时间上看,算法5.2有4个并列的单循环,其时间复杂度为O(nu+tu)。 在M的非零元个数tu和mu*nu等数量级时,其时间复杂度为O(mu*nu),和经典算法的时间复杂度相同。
数组A[0..5,0..6]的每个元素占5个单元,将其按列优先次序存储在起始地址为1000的连续内存单元中,则元素a[5][5]的地址为____。数组A[0..5,0..6]的每个元素占5个单元,将其按列优先次序存储在起始地址为1000的连续内存单元中,则元素a[5][5]的地址为____。 A. 1175 B. 1180 C. 1205 D.1210 对矩阵进行压缩存储是为了______。 A. 方便运算 B. 节省存储空间 C. 方便存储 D. 提高运算速度 复 习 题 A B
一个n阶对称矩阵,如果采用压缩存储方式,则容量为______。一个n阶对称矩阵,如果采用压缩存储方式,则容量为______。 A. n2 B. n2/2 C. [(n+1)n]/2 D. [(n+1)2]/2 数组A[1..10,-2..6,2..8]以行优先顺序存储,设第一个元素的首地址为100,每个数据元素占3个单元的存储空间,则元素A[5][0][7]的存储地址为______________。 复 习 题 C 913
5. 若将n阶上三角矩阵A按列优先顺序压缩存放在一维数组B中,A的起始元素a[1][1]存放在B[0]中,则应存放到B[k]中的非零元素a[i][j](1≤j≤n, 1 ≤ i ≤ j)的下标i,j与k的对应关系是 ______。 A. i(i+1)/2+j B. i(i-1)/2+j-1 C. j(j+1)/2+i D. j(j-1)/2+i-1 复 习 题 D
6. 判断以下叙述的正确性。 用一维数组表示矩阵,可以简化对矩阵的存取操作。 对角矩阵的特点是非零元素只出现在矩阵的两条对角线上。 在n(n>3)阶三对角矩阵中,每一行都有3个非零的元素。 稀疏矩阵的特点是矩阵中的元素较少。 复 习 题 ( ) ( ) ( ) ( )