1.24k likes | 1.45k Views
第五章 数组和广义表. 本章主要内容 :. 数组的类型定义. 数组的顺序表示和实现. 稀疏矩阵的压缩存储. 广义表的类型定义. 广义表的表示方法. 广义表操作的递归函数. 本章重点 : 数组的类型定义 稀疏矩阵的压缩存储 广义表的类型定义 本章难点 : 稀疏矩阵的压缩存储 广义表的表示和实现. 第五章 数组和广义表. 数组和广义表可以看成线性表在下述含义上的扩展:表中数据元素本身也是一个数据结构。 几乎所有的程序设计语言都把数组类型设为固定类型。 本章以抽象数据类型的形式讨论数组的定义和实现。. 5.1 数组的类型定义. ADT Array {
E N D
本章主要内容: • 数组的类型定义 • 数组的顺序表示和实现 • 稀疏矩阵的压缩存储 • 广义表的类型定义 • 广义表的表示方法 • 广义表操作的递归函数
本章重点: • 数组的类型定义 • 稀疏矩阵的压缩存储 • 广义表的类型定义 本章难点: • 稀疏矩阵的压缩存储 • 广义表的表示和实现
第五章 数组和广义表 • 数组和广义表可以看成线性表在下述含义上的扩展:表中数据元素本身也是一个数据结构。 • 几乎所有的程序设计语言都把数组类型设为固定类型。 • 本章以抽象数据类型的形式讨论数组的定义和实现。
5.1 数组的类型定义 ADT Array { 数据对象: D={aj1,j2, ...,,ji,jn| ji =0,...,bi -1, i=1,2,..,n } 数据关系: R={R1, R2, ..., Rn} Ri={<aj1,... ji,... jn, aj1, ...ji +1, ...jn> | 0 jk bk -1, 1 k n 且k i, 0 ji bi -2, i=2,...,n } } ADT Array 基本操作:
二维数组的定义: 数据对象: D = {aij | 0≤i≤b1-1, 0 ≤j≤b2-1} 数据关系: R = { ROW, COL } ROW = {<ai,j,ai+1,j1>| 0≤i≤b1-2, 0≤j≤b2-1} COL = {<ai,j,ai,j+1>| 0≤i≤b1-1, 0≤ j≤b2-2}
我们可以把二维数组看成这样一个定长线性表:它的每个数据元素也是一个定长线性表。我们可以把二维数组看成这样一个定长线性表:它的每个数据元素也是一个定长线性表。 • 可以把二维数组看成一个一维数组,其中每个数据元素也是一维数组。
如下所示二维数组,以m行n列的矩阵表示: • A= a00 a01 a02 … a0,n-1 • a10 a11 a12 … a1,n-1 • . . . . • . . . . • am-1,0 ……. am-1,n-1 • 它可以看成一个线性表: • A=(A0,A1,A2,…Ap) (p=m-1或n-1) • Aj可以看成列向量形式=(a0j,a1j,…am-1j) • 或者Ai是行向量形式列表=(ai0,ai1,..ai,n-1)
基本操作: 数组一旦被定义,它的维数和上、下界 就不会再改变,因此,除了结构的初始化和销毁之外,数组只有存取元素和修改元素的操作。
基本操作: InitArray(&A, n, bound1, ..., boundn) DestroyArray(&A) Value(A, &e, index1, ..., indexn) Assign(&A, e, index1, ..., indexn)
InitArray(&A, n, bound1, ..., boundn) 操作结果:若维数 n 和各维长度合法, 则构造相应的数组A,并 返回OK。
Value(A, &e, index1, ..., indexn) 初始条件:A是n维数组,e为元素变量, 随后是n 个下标值。 操作结果:若各下标不超界,则e赋值为 所指定的A 的元素值,并返 回OK。
Assign(&A, e, index1, ..., indexn) 初始条件:A是n维数组,e为元素变量, 随后是n 个下标值。操作结果:若下标不超界,则将e的值赋 给所指定的A的元素,并返回 OK。
5.2 数组的顺序表示和实现 类型特点: 1) 只有引用型操作,没有加工型操作; 2) 数组是多维的结构,而存储空间是 一个一维的结构。 • 有两种顺序映象的方式: • 1)以行序为主序(低下标优先); • 2)以列序为主序(高下标优先)。
0 a00 b1*b2数组 按行序为主序存放 1 a01 ……. m-1 a0n m a00 a01 …….. a0n a10 a10 a11 …….. a1n a11 …….. …………………. a1n am0 am1 …….. amn ………. am0 Loc(aij)=Loc(a00)+[i*b2+j]*l am1 …….. m*n-1 amn
0 a00 b1*b2数组按列序为主序存放 1 a10 ……. m-1 am0 m a00 a01……..a0n a01 a10 a11 ……..a1n a11 …….. …………………. am1 am0am1…….. amn ………. a0n Loc(aij)=Loc(a00)+[j*b1+i]*l a1n …….. m*n-1 amn
以“行序为主序”的存储映象 二维数组A中任一元素ai,j的存储位置 LOC(i,j) = LOC(0,0) + (b2×i+j)× L 称为基地址或基址。
推广到一般情况,可得到 n 维数组数据元素存储位置的映象关系 n LOC(j1, j2, ..., jn ) = LOC(0,0,...,0) + ∑ ci ji = LOC(0,0,...,0) +(b2*b3*b4*…*bn*j1+b3 *b4*b5*…*bn*j2+…+bn*jn-1+jn)L =1 i 称为 n 维数组的映象函数。数组元素 的存储位置是其下标的线性函数。
例:以行序为主序存储的整数数组A(9*3*5*8),第一个元素的地址是100,每个整数占4个字节,问a(3125)的地址是多少?例:以行序为主序存储的整数数组A(9*3*5*8),第一个元素的地址是100,每个整数占4个字节,问a(3125)的地址是多少? • LOC(a[3125])=100+(3*5*8*3+5*8*1+8*2+5)*4=1784
5.3 稀疏矩阵的压缩存储 在这里我们考虑的不是矩阵本身,而是如何 存储矩阵的元,从而使矩阵的各项运算能够进行。 用高级语言编程序的时候,通常用二维数组来存放矩阵的元。 但是,有时一些阶数很高的矩阵,里面存了很多值相同的元,或者零元,所以要对矩阵进行压缩存储。
5.3 稀疏矩阵的压缩存储 何谓稀疏矩阵? 假设 m 行 n 列的矩阵含 t 个非零元素,则称 为稀疏因子。 通常认为 0.05 的矩阵为稀疏矩阵。
5.3 稀疏矩阵的压缩存储 何谓压缩存储? 就是为多个值相同的元分配一个存储空间;对零元不分配空间。
有两类稀疏矩阵: 1) 特殊矩阵 非零元在矩阵中的分布有一定规则 例如: 三角矩阵 对角矩阵 2) 随机稀疏矩阵 非零元在矩阵中随机出现
特殊矩阵的压缩存储 以n阶对称矩阵为例: 在一个n阶方阵A中,若元素满足下述性质: aij=aji(1≦i,j≦n) 则称A为n阶对称矩阵。如图5.1 1 5 1 3 7 a11 5 0 8 0 0 a21 a 22 1 8 9 2 6 a31 a32 a33 3 0 2 5 1 ……………….. 7 0 6 1 3 an1 an2 an3 …a nn 图 5.1 对称矩阵
n阶对称矩阵中的元素关于主对角线对称,故只要存储矩阵中上三角或下三角中的元素,让每两个对称的元素共享一个存储空间,这样,能节约近一半的存储空间。n阶对称矩阵中的元素关于主对角线对称,故只要存储矩阵中上三角或下三角中的元素,让每两个对称的元素共享一个存储空间,这样,能节约近一半的存储空间。 不失一般性,我们按“行优先顺序”存储主对角线(包括对角线)以下的元素。
在这个下三角矩阵中,第i行恰有i个元素,元素总数为n(n+1)/2,这样就可将n2个数据元素压缩存储在n(n+1)/2个存储单元中。在这个下三角矩阵中,第i行恰有i个元素,元素总数为n(n+1)/2,这样就可将n2个数据元素压缩存储在n(n+1)/2个存储单元中。 假设以一维数组va作为n阶对称矩阵A的压缩存储单元,k为一维数组va的下标序号,aij为n阶对称矩阵A中i行j列的数据元素(其中1≦i,j≦n ),其数学映射关系为: i(i-1)/2+j-1 当i≧j j(j-1)/2+i-1 当i<j k=
a11 0 0…….. 0 a21 a220…….. 0 …………………. 0 an1 an2 an3…….. ann 按行序为主序: …... a11 a21 a22 a31 a32 an1 ann …... k=0 1 2 3 4 n(n-1)/2 n(n+1)/2-1 i(i-1) Loc(aij)=Loc(a11)+[( +(j-1)]*l 2 • 三角矩阵
a11a12 0 …………… . 0 a21 a22a230…………… 0 0a32 a33a340……… 0 …………………………… 00… an-1,n-2 an-1,n-1an-1,n 00… …an,n-1 ann. …... a11 a12 a21 a22 a23 ann-1 ann 按行序为主序: …... k=0 1 2 3 4 n(n-1)/2 n(n+1)/2-1 • 对角矩阵 Loc(aij)=Loc(a11)+2(i-1)+(j-1)
特殊矩阵中非零元的分布都有一个明显的规律,所以可以压缩存储到一维数组中,并找出在一维数组中的对应关系。特殊矩阵中非零元的分布都有一个明显的规律,所以可以压缩存储到一维数组中,并找出在一维数组中的对应关系。
随机稀疏矩阵的压缩存储方法: 一、三元组顺序表 二、行逻辑联接的顺序表 三、 十字链表
一、三元组顺序表 #define MAXSIZE 12500 typedef struct { int i, j; //该非零元的行下标和列下标 ElemType e; // 该非零元的值 } Triple; // 三元组类型 typedef struct{ Triple data[MAXSIZE + 1]; int mu, nu, tu; } TSMatrix; // 稀疏矩阵类型
例1:写出下图5.3所示稀疏矩阵的压缩存储形式。例1:写出下图5.3所示稀疏矩阵的压缩存储形式。 1 2 3 4 5 6 1 2 3 4 5 6 图5.3 012 9 0 0 0 00 0 0 0 0 -3 0 0 0 14 0 0 0 24 0 0 0 018 0 0 0 0 150 0 -7 0 0 解:用三元组线性表表示: {{1,2,12},{1,3,9},{3,1,-3},{3,5,14}, {4,3,24},{5,2,18},{6,1,15},{6,4,-7}}
00 0 0 00 0 0 0 0 0 0 00 0 0 02 0 0 00 0 0 120 0 0 00 0 0 3 0 0 0 00 0 4 00 6 0 160 0 0 例2:下面的三元组表表示一个稀疏矩阵,试还原出它的稀疏矩阵。
用常规的二维数组表示时的算法 for (col=1; col<=nu; ++col) for (row=1; row<=mu; ++row) T[col][row] = M[row][col]; 其时间复杂度为: O(mu×nu)
用“三元组”表示时如何实现? • 先分析转置的过程:能否将行、列直接互换??
用“三元组”表示时如何实现? 2 1 14 1 2 14 5 1 -5 1 5 -5 2 2 -7 2 2 -7 1 3 36 3 1 36 4 3 28 3 4 28
有两种解决方法: • 1、按照矩阵M的列序来转置。
用“三元组”表示时如何实现? 1 3 36 1 2 14 2 1 14 1 5 -5 2 2 -7 2 2 -7 4 3 28 3 1 36 5 1 -5 3 4 28
cpot[1]=1; cpot[col]=cpot[col-1]+num[col-1];(2col a.nu) 2、快速转置 即按a中三元组次序转置,转置结果放入b中恰当位置 此法关键是要预先确定M中每一列第一个非零元在b中位置,为确定这些位置,转置前应先求得M的每一列中非零元个数 实现:设两个数组 num[col]:表示矩阵M中第col列中非零元个数 cpot[col]:指示M中第col列第一个非零元在b中位置 显然有:
cpot[1]=1; cpot[col]=cpot[col-1]+num[col-1]; col 3 6 7 1 2 4 5 2 num[col] 1 0 2 2 1 0 cpot[col] 实现:设两个数组 num[col]:表示矩阵M中第col列中非零元个数 cpot[col]:指示M中第col列第一个非零元在b中位置 显然有: 5 8 9 1 3 7 8
cpot[1] = 1; for (col=2; col<=M.nu; ++col) cpot[col] = cpot[col-1] + num[col-1];
i j v 0 1 2 3 4 5 6 7 8 p p p p p p p p 678 1 2 12 1 3 9 3 1 -3 col 7 6 4 1 5 2 3 3 6 14 num[col] T 0 2 2 2 1 1 0 4 3 24 9 cpot[col] 8 1 5 8 3 7 5 2 18 6 1 15 6 4 -7 2 4 6 9 3 5 7 i j v 768 0 1 2 3 4 5 6 7 8 1 3 -3 1 6 15 2 1 12 2 5 18 3 1 9 3 4 24 4 6 -7 6 3 14 M
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &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]; cpot[1] = 1; for (col=2; col<=M.nu; ++col) cpot[col] = cpot[col-1] + num[col-1]; for (p=1; p<=M.tu; ++p) { } } // if return OK; } // FastTransposeSMatrix 转置矩阵元素
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]
分析算法FastTransposeSMatrix的时间复杂度: for (col=1; col<=M.nu; ++col) … … for (t=1; t<=M.tu; ++t) … … for (col=2; col<=M.nu; ++col) … … for (p=1; p<=M.tu; ++p) … … 时间复杂度为: O(M.nu+M.tu)
二、行逻辑联接的顺序表 三元组顺序表又称有序的双下标法,它的特点是,非零元在表中按行序有序存储,因此便于进行依行顺序处理的矩阵运算。然而,若需随机存取某一行中的非零元,则需从头开始进行查找。
修改前述的稀疏矩阵的结构定义,增加一个数据成员rpos,存放各行第一个非零元在三元组表中的位置.其值在稀疏矩阵的初始化函数中确定。修改前述的稀疏矩阵的结构定义,增加一个数据成员rpos,存放各行第一个非零元在三元组表中的位置.其值在稀疏矩阵的初始化函数中确定。 #define MAXMN 500 typedef struct { Triple data[MAXSIZE + 1]; int rpos[MAXMN + 1]; int mu, nu, tu; } RLSMatrix; // 行逻辑链接顺序表类型