Transcript Document

第五章 数组和广义表
一、教学内容:
 数组的定义和顺序存储方式;
 特殊矩阵及稀疏矩阵的压缩存储;
 广义表的概念、表示及基本操作;广义表
存储结构的实现。
第五章 数组和广义表
二、教学要求:
 掌握一维数组以及多维数组的存储和表示
方法,能计算二维数组任一元素的存贮地
址;
 掌握对特殊矩阵进行压缩存储时的下标变
换公式,掌握稀疏矩阵的三元组表示方法
及矩阵转置算法思想。
 掌握广义表的结构特点及其基本运算,了
解广义表存储表示方法。
第五章 数组和广义表
教学重点:
数组的定义 , 数组的顺序表示方法,矩阵
的压缩存储。
教学难点:
矩阵的压缩存储、广义表存储结构、广义
表的递归算法。
第五章 数组和广义表
5.1 数组的定义
5.2 数组的顺序表示和实现(重点和难点)
5.3 矩阵的压缩存储
(重点和难点)
5.3.1 特殊矩阵
5.3.2 稀疏矩阵
5.4 广义表的定义及存储结构(重点和难点)
5.1 数组
数组可看成是一种特殊的线性表,其特殊在于,
表中的数据元素本身也是一种线性表。
一维数组:A1=(a0,a1,a2,……,an-1)
二维数组:
a00
a01 …… a0n-1
A2=
……
am-10 am-11
am-1n-1
又可表示为:
A2=( a0,a1,a2,……,an-1 )
其中 ai=(a0i,a1i,……,am-1i)为列向量。
5.1 数组
N维数组:b1×b2×b3×……×bn
也可以表示为一个线性表
( a0,a1,a2,……,abn -1 )
表中的每个元素均为一个N-1维的数组。
数组的抽象数据类型(了解)
数组一旦被定义,它的维数和维界就不再
改变。因此,除了结构的初始化和销毁之
外,数组只有存取元素和修改元素值的操
作。
第五章 数组和广义表
5.1 数组的定义
5.2 数组的顺序表示和实现
5.3 矩阵的压缩存储
5.3.1 特殊矩阵
5.3.2 稀疏矩阵
5.4 广义表的定义及存储结构
5.2 数组的顺序表示和实现
类型特点:
1) 只有引用型操作,没有加工型操作;
2) 数组是多维的结构,而存储空间是 一个一
维的结构。
有两种顺序映象的方式:
1)以行序为主序;
2)以列序为主序;
数组的顺序存储方式
⑴行优先顺序——将数组元素按行排列,第i+1个
行向量紧接在第i个行向量后面。以二维数组为
例,按行优先顺序存储的线性序列为:
a11,a12,…,a1n,a21,a22,…a2n,……,am1,am2,…,amn
在PASCAL、C语言中,数组就是按行优先顺序
存储的。
⑵列优先顺序——将数组元素按列向量排列,第
j+1个列向量紧接在第j个列向量之后,A的m*n个
元素按列优先顺序存储的线性序列为:
a11,a21,…,am1,a12,a22,…am2,……,an1,an2,…,anm
在FORTRAN语言中,数组就是按列优先顺序存储的。
数组元素地址计算
例如: 数组A b1×b2
a0,0 a0,1 a0,2
a1,0 a1,1 a1,2
a0,0 a0,1 a0,2 a1,0 a1,1 a1,2
L
二维数组A中任一元素ai,j 的存储位置
LOC(i,j) = LOC(0,0) + (b2×i+j)×L
称为基地址或基址。
数组元素地址计算
同样,三维数组Ab1×b2×b3按“行优先顺序”存
储,元素aijk的地址计算函数为:
LOC(aijk)=LOC(a000)+(i*b2*b3+j*b3+k)*L
结论:
只要知道开始元素的存放地址(即基地址),维
数和每维的上、下界,以及每个数组元素所占用
的单元数,就可以将数组元素的存放地址表示为
其下标的线性函数。因此,数组中的任一元素可
以在相同的时间内存取,即顺序存储的数组是一
个随机存取结构。
了解数组的顺序存贮表示和实现
例1、一个二维数组A,行下标的范围是1到6,列下标
的范围是0到7,每个数组元素用相邻的6个字节存储,
存储器按字节编址。那么,这个数组的体积是 288 个
字节。
例2:已知二维数组Am,m按行存储的元素地址公式是:
Loc(aij)= Loc(a11)+[(i-1)*m+(j-1)]*K , 按列存储
的公式是?
Loc(aij)=Loc(a11)+[(j-1)*m+(i-1)]*K
例3、设数组a[1…60, 1…70]的基地址为2048,每个
元素占2个存储单元,若以列序为主序顺序存储,则元
8950
素a[32,58]的存储地址为
。
答:请注意审题!
利用列优先通式:
LOC(aij)=LOC(ac1,c2)+[(j-c2)*b1+i-c1)]*L
第五章 数组和广义表
5.1 数组的定义
5.2 数组的顺序表示和实现
5.3 矩阵的压缩存储
5.3.1 特殊矩阵
5.3.2 稀疏矩阵
5.4 广义表的定义及存储结构
5.3 矩阵的压缩存储
矩阵的一般存贮表示?元素的访问?
矩阵类似二维数组,存储方式同二维数组。
1 5 1 3 7
0 0 3 0 0
5 0 8 0 0
4 0 0 0 5
A= 1 8 9 2 6
B= 0 0 0 0 7
3 0 2 5 1
6 0 0 0 3
7 0 6 1 3
但以二维数组表示的特殊矩阵有重复元素值反复存储的缺点,
及高阶的稀疏矩阵时产生的问题:
1) 零值元素占了很大空间;
2) 计算中进行了很多和零值的运算,遇除法,还需判别除数
是否为零。
5.3.1特殊矩阵
所谓特殊矩阵是指非零元素或零元素的分布有一定规律的矩阵,
下面我们讨论几种特殊矩阵的压缩存储。
1、对称矩阵
在一个n阶方阵A中,若元素满足下述性质:
aij=aji
1≦i,j≦n
则称A为对称矩阵。如下图便是一个5阶对称矩阵。
1
5
1
3
7
5
0
8
0
0
1
8
9
2
6
对称矩阵A
3
0
2
5
1
7
0
6
1
3
a11
a21 a 22
a31 a32 a33
………………..
an1 a n2
a n3 …
a nn
在这个下三角矩阵中,第i行恰有i个元素,元素
总数为:
i=n
∑ (i)=(1+2+3+……+n)=n(n+1)/2
i=1
压缩的方法是首先将二维关系映射成一维关系,并只
存储其中必要的n(n+1)/2个(主对角线和下三角)元
素内容,这些元素的存储顺序以行为主序。
因此,我们可以按图中箭头所指的次序将这些元素存
放在一个向量sa[0..n(n+1)/2-1]中。
0
1
2
3
a11 a21 a22 a31
n(n-1)/2
……
an, 1
n(n+1)/2-1
……
an,n
为了便于访问对称矩阵A中的元素,我们必须在
aij和sa[k]之间找一个对应关系。
若i≧j,则ai j在下三角形中。 ai j之前的i-1行(从第1
行到第i-1行)一共有1+2+…+(i-1)=i(i-1)/2个元
素,在第i行上, a i j之前恰有j-1个元素(即
ai1,ai2,ai3,…,aij-1),因此有:
k=i*(i-1)/2+j-1
i≧j
若i<j,则aij是在上三角矩阵中。因为aij=aji,所以只
要交换上述对应关系式中的i和j即可得到:
k=j*(j-1)/2+i-1
i<j
2、三角矩阵
以主对角线划分,三角矩阵有上三角和下三角两种。
上三角矩阵如图所示,它的下三角(不包括主对角线)
中的元素均为常数。下三角矩阵正好相反,它的主对
角线上方均为常数,如图所示。在大多数情况下,
三角矩阵常数为零。
a11 a12 … a 1 n
a11 c
… c
c
a22 … a 2 n
a21 a22 … c
…………………..
……………..
c
c …ann
an1 an2 … ann
(a)上三角矩阵
(b)下三角矩阵
三角矩阵
三角矩阵存储
对上三角阵只需存上三角部分,一维数组B中从0号位
置开始存放,并按行优先存储,如下图所示:
0
1 … n-1 n
n+1 … 2n-2 … n(n+1)/2-1
a1,1 a1,2 … a1,n a2,2 a2,3 … a 2, n
……
an,n
则对矩阵A的任意矩阵元素aij(i≤j),在按行优先存储的情况
下,它在一维数组B中对应的存储位置为:
LOC(i,j)=n+(n-1)+(n-2)……+(n-(i-1)+1)+(j-i)
=(2n-i+2)(i-1)/2+j-i
3、对角矩阵(了解)
对角矩阵中,所有的非零元素集中在以主对
角线为中心的带状区域中,即除了主对角线和主
对角线相邻两侧的若干条对角线上的元素之外,
其余元素皆为零。下图给出了一个三对角矩阵,
a00 a01
a10 a11 a12
a21 a22 a23
…. ….. ….
an-2 n-3 an-2 n-2 an-2 n-1
an-1 n-2 an-1 n-1
对角矩阵可按行优先顺序或对角线的顺序,将
其压缩存储到一个向量中,并且也能找到每个非
零元素和向量下标的对应关系。
(0≤i≤n-1,i-1≤j ≤i+1) LOC(i,j)=2i+j
5.3.2 稀疏矩阵
什么是稀疏矩阵?简单说,矩阵非零元素
远远小于矩阵元素的总数,则称为稀疏矩
阵.
在存储稀疏矩阵时,为了节省存储单
元,只存储非零元素。故用一个三元组
(i,j,aij)来存储矩阵的一个非零元。
一个三元组(i,j,aij)唯一确定了矩阵的
一个非零元。因此,稀疏矩阵可由表示非
零元的三元组及其行列数唯一确定。
例如,下列三元组表
((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 0 0 0 0 0
-3 0 0 0 0 14 0
0 0 24 0 0 0 0
0 18 0 0 0 0 0
15 0 0 –7 0 0 0
稀疏矩阵M
而由上述三元组表的不同表示方法可引
出稀疏矩阵不同的压缩存储方法。
一、三元组顺序表(理解)
二、行逻辑链接的顺序表(了解)
三、十字链表(了解)
一、三元组顺序表(以行为主的顺序)
 0 14 0 0  5
0 7 0 0 0 


36 0 0 28 0 
i
1
1
2
3
3
j
2
5
2
1
4
v
14
-5
-7
36
28
三元组顺序表存储表示
#define MAXSIZE 12500
//非0元个数最大值
typedef int ElemType; //元素值类型
typedef struct{
int
i,j;
//非0元的行列下标值
ElemType e;
}Triple; //三元组类型
typedef struct{
Triple data[MAXSIZE+1]; //data[0]不用
int mu,nu,tu;//行数、列数、非0元个数
}TSMatrix; //三元组顺序表类型
矩阵的转置运算
 普通矩阵的转置如何实现?
aij和aji互换.
 算法实现思想?
在转置之前需要完成的任务:矩阵信息
存贮到在计算机中,运用实际的存贮形式来
完成转置任务,结果需要显示给用户.
矩阵的转置运算算法思想
一个m×n的矩阵A,它的转置B是一个
n×m的矩阵,且a[i][j]=b[j][i],
1≦i≦m,1≦j≦n,即A的行是B的列,A的
列是B的行。
将A转置为B,就是将A的三元组表
a.data置换为表B的三元组表b.data,如果
只是简单地交换a.data中i和j的内容,那
么得到的b.data将是一个按列优先顺序存
储的稀疏矩阵B,要得到按行优先顺序存储
的b.data,就必须重新排列三元组的顺
序。
例如:A=
i
1
1
2
3
3
 0 14 0 0  5
0 7 0 0 0 


36 0 0 28 0 
j
2
5
2
1
4
v
14
-5
-7
36
28
B=
i
2
5
2
1
4
0
14
0
0
 -5
j
1
1
2
3
3
0
-7
0
0
0
v
14
-5
-7
36
28
36
0
0
28
0





矩阵的转置运算算法思想
要得到转置结果,结合转置前后三元组信息的特点,
用哪些方法可以得到转置结果?
两种方法:
按照b.data中三元组的次序依次在a.data中找相应的
三元组进行转置
按照a.data中三元组的次序进行转置,并将转置后的
三元组置入b.data中恰当位置
由于A的列是B的行,因此,按a.data的列序
转置(先放第一列所有非0员,再放下一列所有非0
元),所得到的转置矩阵B的三元组表b.data必定
是按行优先存放的。
 0 14 0 0  5
0 7 0 0 0 


36 0 0 28 0 
矩阵的转置
运算算法思想
0
14
0
0
 -5
0
-7
0
0
0
36
0
0
28
0
对A中的每一列 col(1≦col≦n),通过从头至
尾扫描三元表a.data,找出所有列号等于col的那些
三元组(col从1依次递增到n),将它们的行号和列
号互换后依次放入b.data中,即可得到B的按行优先
的压缩存储表示。
i
1
1
2
3
3
j
2
5
2
1
4
v
14
-5
-7
36
28
i
1
2
2
4
5
j
3
1
2
3
1
v
36
14
-7
28
-5





算法实现(理解算法,程序填空):p99
void TransposeSMatrix(TSMatrix M, TSMatrix &T)
{ T.mu=M.nu;
T.nu=M.mu; T.tu=M.tu; //行列互换
if(T.tu!=0) //非0元个数不为0则进行非0元素转置
{ q=1;
//转置矩阵T对应三元组中最新非0元的位置
for(col=1;col<=M.nu;col++) //按照M的列进行扫描转置
for(p=1;p<=M.tu;p++) //对于每一行,都从三元组所有元素中扫描
if(M.data[p].j==col) //col值比较,该列出现一个非0元,信息加入
{ 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++; //q指向下一个位置
}
}
}
算法分析
该算法主要的工作是在p和col的两个循环中完
成的,故算法的时间复杂度为O(nu*tu)。而一般
传统矩阵的转置算法为:
for(col=1;col<=nu;++col)
for(row=1;row<=mu;++row)
t[col][row]=m[row][col];
其时间复杂度为O(mu*nu)。当非零元素的个数tu
和mu*nu同数量级时,该算法的时间复杂度为
O(mu*nu2)。因此,此算法仅适用于tu<=mu*nu的
情况。
快速转置的算法
设置两个一维数组num[1..n]和cpot[1..n]
num[col]:表示M中第col列非零元素的个数
cpot[col]:指示M中第col列第一个非0元的存贮位置
显然有:
cpot[1]=1
cpot[col]=cpot[col-1]+num[col-1] 2<=col<=a.nu
快速转置的算法
算法思想:
对A进行扫描,按A提供的列号一次确定装入B
的一个三元组的位置。具体实施如下:一遍扫描
先确定三元组的位置关系,二次扫描由位置关系
装入三元组。可见,位置关系是此种算法的关键。
第一遍扫描A
1.确定A的每一列即B的每一行的元素个数,
2.确定B每一行的第一个非0元应该存贮的位置
第二遍扫描:
对A进行扫描,对当前非0元进行定位和存贮。
快速转置的算法
图5.5(P97)中的矩阵M和相应的三元组A可以求得
num[col]和 cpot[col]的值如下:
col
num[col]
cpot[col]
1
2
1
2
2
3
3
2
5
4
1
7
5
0
8
6
1
8
7
0
9
下面通过num和cpot数组值来观察几个非0元的存入过程。
i
1
1
3
3
4
5
6
6
j
2
3
1
6
3
2
1
4
v
12
9
-3
14
24
18
15
-7
Col 序号i
1 1
1
2 1
3 2
2
4 2
5 3
3
6 3
7 4
4
8 6
6
9
j
3
6
1
5
1
4
6
3
v
-3
15
12
18
9
24
-7
14
当对A扫描并将
所有元素存入B
后,B的每行的
位置与下一行
的初始值重合
快速转置的算法实现(P100)
void FastTransposeSMatrix(TSMatrix M, TSMatrix
&T)
{
T.mu=M.nu; //行列互换
T.nu=M.mu;
T.tu=M.tu;
if(T.tu!=0)
{
for(col=1;col<=M.nu;++col) //设置num初值
num[col]=0;
//扫描第一遍M三元组信息,计算num
for(p=1;p<=M.tu;++p)
num[M.data[p].j]++;
快速转置的算法实现(P100)
cpot[1]=1; //计算cpot值
for(p=2;p<=M.nu;p++)
cpot[p]=cpot[p-1]+num[p-1];
//第二遍扫描M三元组,一次性定位和转置
}
for(p=1;p<=M.tu;++p) {
col=M.data[p].j; //取出该元素的列号
k=cpot[col]; //确定在T三元组中的应放位置
T.data[k].i=M.data[p].j; //放置信息
T.data[k].j=M.data[p].i;
T.data[k].e=M.data[p].e;
cpot[col]++; //为该列出现的下一个非0元定位}
}
快速转置的算法分析
1、多用了两个辅助向量;
2、时间复杂度:O(nu+tu);
3、当非零元素的个数tu和mu*nu同数量级
时,时间复杂度:O(mu×nu)
三元组顺序表特点
非零元在表中按行序有序存储,因此
便于进行依行顺序处理的 矩阵运算;然
而,若需按行号存取某一行的非零元,则
需从头开始进行查找。另当矩阵的非零元
个数和位置在操作过程中变化较大时,就
不适宜了。
解决方法:三元组采用其他表示方法
行逻辑链接的顺序表和十字链表
作业
1、设定二维整数数组B[0..m-1,0..n-1]的
数据在行、列方向上都按从小到大的顺序
排序,且整型变量x中的数据在B中存在。
试设计一个算法,找出一对满足B[i][j]=x
的i、j值。要求比较次数不超过m+n。
2、编写一个算法,计算一个三元组表表示
的稀疏矩阵的对角线元素之和。
第五章 数组和广义表
5.1 数组的定义
5.2 数组的顺序表示和实现
5.3 矩阵的压缩存储
5.3.1 特殊矩阵
5.3.2 稀疏矩阵
5.4 广义表的定义及存储结构
5.4
广义表
1、定义:
广义表(Lists,又称列表)是线性表的推广。
广义表是n(n>=0)个元素a1,a2,a3,…,an的有限序
列,其中ai或者是原子项,或者是一个广义表。
通常记作LS=(a1,a2,a3,…,an)。LS是广义表
的名字,n为它的长度。若ai是广义表,则称
它为LS的子表。
广义表是递归定义的线性结构, LS = ( 1, 2, , n )
其中:i 或为原子 或为广义表
例:(1)A=()
(2)B=(e) 例(2)的表头为e,表尾为()。
(3) C=(a,(b,c,d)) 例(3)的表头为a,表尾为
((b,c,d))。
(4) D=(A,B,C)
(5) E=(a,E)
例(4)的表头为A,表尾为(B,C)。
习惯上,大写字母表示名称,小写字母表示原子。
当广义表非空时,第一个元素称为表头,其余元素
组成的表称为表尾。
2、广义表的特点
列表是一个多层次的结构;
 列表可为其他列表所共享;
 列表可以是一个递归的表
递归表的深度是无穷值,长度是有限值。

3、广义表的基本操作
(1)求表长
(3)求表头
(2)求表深度
(4)求表尾
例如: D = ( E, f ) = ((a, (b, c)),f )
Length(D)=2 Head( D ) = E Tail( D ) = ( f )
Head( E ) = a
Tail( E ) = ( ( b, c) )
Length(E)=2
Depth(E)=2
Head( (( b, c)) ) = ( b, c) Tail( (( b, c)) ) = ( )
Length( ((b,c)) )=1
Depth( ((b,c)) )=2
4、广义表的存储结构
由于广义表中的数据元素可以具有不同
结构(原子或列表),因此难以用顺序存
储结构表示,通常用链式存储结构,每个
数据元素可用一个结点表示。有两种结点
结构:原子和列表。
tag=0
atom
原子
tag=1
列表
hp
tp
例如:C=(a,(b,c,d))
C
1
1
0 a
∧
1
1
0 b
1
0 c
∧
0 d
5、广义表的递归算法



求广义表的深度
复制广义表
建立广义表的存储结构
求广义表的深度的递归算法
C=(a,(b,c,d))
广义表LS=(a1,a2,……,an)的深度
DEPTH(LS)的递归定义为:
基本项: DEPTH(LS)=1 当LS为空表时
DEPTH(LS)=0 当LS为原子时
归纳项: DEPTH(LS)
=1+MAX{DEPTH(ai)}
n≥1
1≤i≤n
本章小结
问题1:数组是一种逻辑结构还是物理结构?
问题2:数组的逻辑结构是什么?
问题3:数组的基本操作有哪些?
问题4:矩阵可采用什么存储结构?矩阵的基本运算有
哪些?
问题5:广义表是线性结构还是非线性结构?为什么?
问题6:广义表有哪些特性?
问题7:广义表的基本操作有哪些?