Transcript Document

第四章
第一节
第二节
第三节
数组与特殊矩阵
数组
特殊矩阵的压缩存储
稀疏矩阵
本 章 小 结
实
训
思 考 与 习 题
第四章 数组与特殊矩阵
学习要求:
数组是一种比较常见的数据结构,本章的目标
是让读者了解数组的特性、数组的表示方法,掌握
数组的逻辑结构及内存的存储结构,熟悉几种特殊
矩阵的压缩存储方法。
主要内容:
本章主要讨论数组的基本概念和逻辑结构、存
储方式以及几种特殊矩阵的压缩存储方法。
第一节
数组
数组是人们很熟悉的数据类型,几乎所有的程序设计语言中都设有数
组类型。我们把多维数组看成是广义的线性表,即看成是这样一个线性表:
它的每一个数据元素都是一个定长线性表,或理解成多维数组对应的线性
表中的数据元素本身又是一个线性表。本节重点讨论二维数组的逻辑结构
及其内存映像,多维数组可以在二维数组的分析基础上加以推广。
一、数组的基本概念
高级语言中的数组在计算机内是用一批连续的内存单元来表示的,称
为数组的顺序存储结构。在实际使用中还可以根据需要选择数组的其他存
储方式。
数组是由下标(index)和值(value)组成的序对的集合。在数组中,
一旦给定下标,都存在一个与其相对应的值,这个值就称为数组元素。
数组是一个固定格式和数量的数据有序集,每一个数据元素用唯一的
一个或一组下标标识,在数组上不能做插入、删除数据元素的操作。数组
一旦被定义,它的维数和维界就不再改变。因此,数组的运算通常
只有两种基本运算:
1. 给定一组下标,存取相应的数据元素。
2. 给定一组下标,修改相应的数据元素的值。
二、数组的逻辑结构
数组中的数据元素均属于同一数据类型。在二维数组中,每个
元素都受行和列关系的约束。设定A是一个二维数组,如图4.1(a)
所示,以m行n列的矩阵形式表示。
 a11 a12
 a 21 a 22
Am  n  
 


am1 am 2
 a1n 
 a 2 n 
  

 amn 
图4.1(a) m行n列的二维数组
Am  n  ((a11a12 a1n), (a21a22 a2n),, (am1am 2 amn ))
图4.1(b) 行向量形式的二维数组
  a11   a12   a1n  
      
a
21
a
22
a
2n

Am  n         
         
      
 am1 am 2  amn  
图4.1(c) 列向量形式的二维数组
它可以看成是一个一维数组,即可看成是一个线性表
A=(a1,a2,…,an),其中,每一个元素ai对应一个列向量形式的线性
表ai=(a1i,a2i,…,ami),如图4.1(c)所示。这样我们就可以把二维数
组看成是由几个列向量组成的线性表了。同样,可以把二维数组
A看成是一个线性表A=(a1, a2 ,…, am),其中,每一个元素aj对应
一个行向量形式的线性表aj=(aj1,aj2,aj3,…,ajn),如图4.1(b)所示。
二维数组可以看作“数据元素是一维数组”的一维数组,三
维数组可以看作“数据元素是二维数组”的一维数组,依此类推。
三、数组的内存映像
现在来讨论数组在计算机中的内存表示。通常,数组在内存被映像
为向量,即向量作为数组的一种存储结构,数组是以顺序存储方式存放
在计算机中的,而随机存取是顺序存储结构的主要特性。
对于一维数组,可根据数组元素的下标得到它的存储地址,按下标
顺序分配即可。
对于二维数组,可用向量(顺序)存储结构来存放。用向量结构来存放
二维数组中的元素,一定要按某种次序将元素排成一个线性序列,顺序
存放的次序有两种规则:
1. 先行后列顺序,或者称为行优先顺序,在PASCAL和C语言中,
数组就是按行优先顺序存储的。
2. 先列后行顺序,或者称为列优先顺序,在FORTRAN语言中,数
组就是按列优先顺序存储的。
例如,B数组是个5行3列的二维数组,对应的两种规则的顺序存储
结构如图4.2所示。
图4.2 B数组的两种顺序存放示意图
以上规则可以推广到多维数组:以行优先顺序的分配规律是:最
右边的下标先变化,即最右边的下标从小到大,循环一遍后,右边的
第2个下标再变化,……,从右向左,最后是左下标。以列优先顺序
的分配规律恰好与之相反:最左边的下标先变化,即最左边的下标从
小到大,循环一遍后,左边的第2个下标再变化,……,从左向右,
最后是右下标。
对于顺序存储的数组,只要知道向量的起始地址,数组的行号数
和列号数,以及每个数组元素所占用的存储单元个数,就可以求得给
定下标的数组元素的存储起始地址。
例如,一个二维数组A(m×n)按行优先存储在向量中。设定数组中
第一个元素的存储起始地址为LOC(a1,1),并已知某个数据元素的下标为i
和j(1≤i≤m,1≤j≤n),及每个数据元素占用的存储单元数为b,就可以计算该
数据元素的存储起始地址:
LOC(ai,j)=LOC(a1,1)+[n*(i-1)+(j-1)]*b
值得一提的是:在C语言中,数组下标的下界是0,因此在C语言中,
如果数组元素从0下标存放起,地址的计算公式是:
LOC(ai,j)=LOC(a0,0)+(i*n+j)*b
若二维数组A(m×n)按列优先顺序存储在向量中,则相应A(i,j)的地址
的计算公式为:
LOC(ai,j)=LOC(a1,1)+[m*(j-1)+(i-1)]*b
或
LOC(ai,j)=LOC(a0,0)+(m*j+i)*b
第二节 特殊矩阵的压缩存储
在科学与工程计算问题中,矩阵是一种常用的数学对象,在用高级语
言程序时,简单而又自然的方法,就是将一个矩阵描述为一个二维数组。
矩阵在这种存储表示之下,可以对其元素进行随机存取,各种矩阵运算也
非常简单。这样,利用上一节的地址计算公式可以快速访问矩阵中的每个
元素。然而,实际应用中会遇到一些特殊矩阵,所谓特殊矩阵,是指矩阵
中值相同的元素或者零元素的分布有一定的规律。例如,对称矩阵、三角
矩阵和带状矩阵都是特殊矩阵。
为了节省存储空间,可以对这些矩阵进行压缩存储。所谓压缩存储就
是为多个值相同的元素只分配给一个存储空间,对零元素不分配存储空间。
由于特殊矩阵中非零元素的分布有明显的规律,因此我们可将其压缩存储
到一个一维数组中,并找到每个非零元素在一维数组中的对应关系。
一、对称矩阵
(一) 对称矩阵
若一个n阶矩阵A中的元素满足下述性质:
aij=aji (1≤i≤n,1≤j≤n)
则称A为n阶对称矩阵。例如A矩阵是一个5阶的对称矩阵,
如图4.3所示。
(二)对称矩阵的压缩存储
对于对称矩阵,可以为每一对对称矩阵元素分配一个存储空间,则
可以将n2个元素压缩存储到n(n+1)/2个空间中,节约了n(n-1)/2个存储
单元。当n较大时,这是相当可观的一部分存储资源。
下面以行优先存储其下三角(包括对角线)中的元素。
在下三角矩阵中共有n(n+1)/2个元素,可开辟一个长度为n(n+1)/2
的一维数组B,然后一行接一行地依次存放对称矩阵中下三角(包括对角
线)部分的元素。经压缩后,存放在一维数组B中的形式如图4.4所示。
图4.4 对称矩阵的压缩存储
显然,对称矩阵A用一维数组B表示后,可以按如下原则访问
对称矩阵A中的第i行、第j行的元素aij。
1 如果aij为下三角部分元素(j≤i),则它存放在一维数组B的第i(i1)/2+j个元素中。
2 如果aij为非下三角元素(j>i),则它可以对应一维数组B的第j(j1)/2+i个元素的值。即有如下关系:
aij=B[i(i-1)/2+j]当j≤i
B[j(j-1)/2+i]当j>i
下三角矩阵A也可以用以列优先的方式压缩在一维数组B中,请
读者自己练习。
若对图4.3的5阶对称矩阵进行以行优先压缩存储,其结果如图4.5
所示。
图4.5对称矩阵A的压缩存储
二、三角矩阵
三角矩阵分为下三角矩阵和上三角矩阵。所谓下(上)三角矩阵是
指矩阵的上(下)三角(不包括对角线)中的元素均为常数c或0的n阶矩阵。
例如,B矩阵是一个下三角矩阵;C矩阵是一个上三角矩阵,如图4.6
所示。
下面分别下面讨论它们的压缩存储方法。
图4.6 三角矩阵
(一)下三角矩阵
下三角矩阵的压缩存储与对称矩阵类似,不同之处在于存完下三角矩
阵中的元素之后,紧接着要存储对角线上方的常量c,因为是同一个常数,
所以再加上一个常数c的存储空间即可,这样一共就存储了n(n+1)/2+1个
元素。
在实际应用中,下三角矩阵一般应采用以行优先压缩存储的方法,因
为在以列优先存储下三角矩阵,访问下三角矩阵中的元素时,其下标运
算要比以行优先压缩存储复杂一些。
(二)上三角矩阵
对于上三角矩阵,压缩存储的思想与下三角矩阵类似,但采用以列优
先存储比较方便,其存储形式如图4.7所示。
图4.7上三角矩阵的压缩存储
在以列优先压缩存储上三角矩阵的情况下,访问上三角矩阵中
第i行、第j行元素aij的公式为:
三、带状矩阵
一个n阶方阵,如果它的全部非零元素落在一个以对角线为中心的
带状区域中,则称该矩阵为带状矩阵。若有一n阶矩阵A为带状矩阵,
如果存在最小正数m ,满足当∣i-j∣≥m 时,aij =0,这时称w=2m1为矩阵A的带宽。如图4.8(a)是一个w=3(m=2)的带状矩阵。带状矩阵
也称为对角矩阵。由图4.8(a)可看出,在这种矩阵中,所有非零元素
都集中在以主对角线为中心的带状区域中,即除了主对角线和它的上
下方若干条对角线的元素外,所有其他元素都为零(或同一个常数c)。
带状矩阵A也可以采用压缩存储。一种压缩方法是将A压缩到一个n
行w列的二维数组B中,如图4.8(b)所示,当某行非零元素的个数小于
带宽w时,先存放非零元素后补零。那么aij可映射为b i′j′,映射
关系为:
另一种压缩方法是将带状矩阵压缩到一维数组C中去,按以行优先的
顺序存储其非零元素,如图4.8(c)所示,按其压缩规律找到相应的访问公
式。
如当w=3时,一维数组C以行优先存放带状矩阵A中的元素aij时,其
访问公式为:
图4.8 带状矩阵及压缩存储
第三节 稀疏矩阵
什么是稀疏矩阵?如果矩阵中非零元素的个数远远小于矩阵元素
的总数,这样的矩阵称为稀疏矩阵。
对于稀疏矩阵,只考虑非零元素的存储。但由于非零元素的分布
没有规律,为了能找到相应的元素,仅存储非零元素的值是不够的,
还要记下它所在的行和列。
下面讨论稀疏矩阵的两种压缩存储方法。
一、稀疏矩阵的三元组表存储
将矩阵中的非零元素所在行、列以及它的值构成一个三元组结
构体(i,j,v)。这样一个三元组(i,j,v)便唯一地确定了矩阵中的一个非零元
素,其中i和j分别表示非零元素的行号和列号,v表示非零元素的值。
将三元组按行优先,同一行中列号从小到大的规律排列,则可得
到一个元素类型是三元组的线性表,称为三元组表。
三元组表是稀疏矩阵的一种顺序存储结构。稀疏矩阵的三元组
存储的数据类型描述如下:
#define MAXSIZE 10000
typedef int datatype;
typedef struct{
int i,j;
/*非零元素的行、列*/
datatype v; /*非零元素值*/
} triple;
/*三元组类型*/
typedef struct{
triple data[MAXSIZE]; /*三元组表*/
int m,n,t;
/*矩阵的行、列及非零元素的个数*/
} tripletable;
这种表示方法,在矩阵很稀疏的情况下,对存储空间的需求量比
一般存储少得多。例如,在图4.9所示的M矩阵中,它有6行、7列。
如果每个元素占2个字节,按一般顺序存储则需要6×7×2=84个字节,
而按三元组顺序存储,假设存放非零元素的行号和列号也各占2个字
节,因有非零元素8个,则仅需8×3×2=48个字节。设定a是类型定
义为tripletable的变量,表示稀疏矩阵M,图4.10是稀疏矩阵M所对应
的三元组存储结构示意图。
图4.9 稀疏矩阵M
图4.10 稀疏矩阵M的三元组结构a
三元组存储结构因以行优先存放,存在以下的规律:元组中的第
一列按行号的顺序由小到大排列,元组中的第二列是列号,列号在行
号相同时也是由小到大排列。
二、稀疏矩阵的十字链表存储
三元组表可以看作是稀疏矩阵的顺序存储,这种表示方式适合求
解稀疏矩阵的转置、相乘等运算。但当矩阵的非零元素的个数和位置
在操作过程中变化较大时,如做一些操作(如加法、乘法)时,这种
表示就十分不便了。为此,稀疏矩阵考虑采用链式存储结构来表示。
同以前链表表示相同,矩阵中的每个非零元素也用一个结点来表
示,结点中除了表示非零元素的行、列、值(row,col,val)三个域
外,还增加了两个指针域:向下域down用于链接同一列中的下一个非
零元素;向右域right用于链接同一行中的下一个非零元素。其结点
结构如图4.11(a)所示。
图4.11 十字链表的结点结构
这样一来,同一行的非零元素通过向右域right链接成一个线性链
表,称为行链表;同一列的非零元素通过向下域down也链接成一个
线性链表,称为列链表。矩阵中的每个非零元素既是某个行链表中的
一个结点,又是某个列链表中的一个结点,整个矩阵构成了一个十字
交叉的链表,这种存储结构称为十字链表。
为了运算上的方便,给十字链表增加了一个表头结点,其结构与
普通的存储非零元素的表头结点相同,如图4.11(b)所示。在实际应用
中,可将表头结点的行、列域都设为零。由于行、列链表的表头结点
的行列域均为零,而且都指向各自链表的第一个非零元素结点,所以
行、列链表的表头结点可以合用。另外,表头结点的值域val没有使用,
可以将其改造成指针域next,用于存放指向下一个表头结点的指针,
并通过该指针域将所有的表头结点也链接成一个链表,在这个均是表
头结点的链表中,再附加一个表头结点,作为“总”的表头结点,其
指针域next指向第一个表头结点,结构和表头结点一样,其中值域
row和col分别表示矩阵的行数和列数,另外两个指针域闲置。
例如,稀疏矩阵A如图4.12所示。
图4.12 稀疏矩阵A
可以用如图4.13所示的十字链表表示。
用十字链表表示稀疏矩阵的结构特点如下:
图4.13稀疏矩阵A的十字链表
(1) 稀疏矩阵的每一行与每一列均用带表头结点的循环链表表示;
(2) 表头结点中的行域与列域的值均置为0(即row=0,col=0);
(3) 行、列链表的表头结点合用,且这些表头结点通过值域(即val)
相链接,并增加一个结点作为它们的表头结点H,其行、列域值分别存
放稀疏矩阵的行数与列数。
由此可以看出,只要给出头指针H的值,便可以扫描到稀疏矩阵中
的任意一个非零元素。
三、稀疏矩阵的转置运算
矩阵的转置运算就是按一定规律变换元素的位置,即把位于(i,j)的
元素换到(j,i)位置上。对于一个m×n的矩阵M,它的转置矩阵是一个
n×m的矩阵N,且Mi,j=Nj,i,
其中,1≤i≤n,1≤j≤m。矩阵转置就是把矩阵元素的行和
列对换。例如,图4.14的稀疏矩阵M的转置矩阵为图4.15
所示的稀疏矩阵N。
图4.14稀疏矩阵M
图 4.15稀疏矩阵M的转置矩阵N
图4.16 稀疏矩阵M的三元组表结构
图4.17 转置矩阵N的三元组表结构
将稀疏矩阵M和M矩阵转置后得到的稀疏矩阵N分别用三元组表
存储,其结构示意图如图4.16、图4.17所示。
a.data和b.data都具有上述三元组存放的规律,这是矩阵转置算
法实现的依据。
算法思路:
(1)矩阵M的行、列转化成矩阵N的列、行;
(2)在a.data中依次找第一列的、第二列的,直到最后一列,并将
找到的每个三元组的行、列交换后,按行号从小到大的顺序存储到
b.data中即可。
用C语言描述稀疏矩阵的转置算法如下:
void transpose (SPMATRIX b, SPMATRIX a)
{
int p,q,col
b.m=a.n;
b.n=a.m;
b.t=a.t;
if (a.t0)
{ q=1;
for (col=1;col<=a.n; col++)
for (p=1; p<=a.t; p++)
if (a.data[p].j==col)
{ b.data[q].j=a.data[p].i;
b.data[q].i=a.data[p].j;
b.data[q].v=a.data[p].v;
q++;
}
}
}
本章小结
本章主要介绍的内容如下:
多维数组在计算机中有两种存放方式:行优先和列优先。
三种特殊矩阵:对称矩阵、三角矩阵和带状矩阵。
对称矩阵关于主对角线对称。为了节省存储单元,可以进行压缩存
储,对角线以上的元素和对角线以下的元素可以共用存储单元,故n×n
的对称矩阵只需n*(n+1)/2个存储单元即可。
三角矩阵有上三角矩阵和下三角矩阵之分,进行压缩存储时,n×n
的三角矩阵只需n*(n+1)/2+1个存储单元即可。
带状矩阵也称为对角矩阵。一种压缩存储方法是将其压缩到一个n行
w列的二维数组B中,另一种压缩存储方法是将带状矩阵压缩到向量中去,
按以行优先,顺序地存储其非零元素,按其压缩规律,找到相应的访问
公式。
稀疏矩阵的非零元素排列无任何规律,进行压缩存储时,可以采用
三元组表示法或十字链表表示法。
实 训
一、实训目的
1.掌握稀疏矩阵的表示方法及其运算的实现。
2.实现稀疏矩阵在三元组表、十字链表等表示下的
各种运算。
二、实训内容
1.问题描述:
稀疏矩阵是指那些多数元素为零的矩阵。利用“稀疏”
特点进行存储和计算可以大大节省存储空间,提高计算效
率。实现一个能进行稀疏矩阵基本运算的运算器。
2.基本要求:
以三元组顺序表表示稀疏矩阵,实现矩阵转置的运算。
稀疏矩阵的输入形式采用三元组表示,而运算结果的矩阵
则以通常的阵列形式列出。
3.测试数据:
三、实训过程
1.算法分析:
(1)首先应输入矩阵的行数和列数,并判别给出的两
个矩阵的行、列数对于所要求作的运算是否相匹配。可设
矩阵的行数和列数均不超过20。
(2)程序可以对三元组的输入顺序加以限制,例如,
按行优先。
(3)在用三元组表示稀疏矩阵时,相加或相减所得结
果矩阵应该另生成,乘积矩阵也可用二维数组存放。
2.算法提示:
#define MAXSIZE 100
struct node
{
int i,j;
/*定义三元组的行、列号*/
int v;
/*三元组的值*/
};
struct sparmatrix
{
int rows,cols;
/ *稀疏矩阵的行、列数*/
int terms;
/*稀疏矩阵的非零元个数*/
struct node data[MAXSIZE];
/*存放稀疏矩阵的三元组表*/
};
void transpose(struct sparmatrix a) /*调用转置算法*/
四、实训总结
通过本实训的实际操作,使读者掌握如何定义稀疏矩
阵的三元组存储结构,在此基础上实现稀疏矩阵的初始化、
取稀疏矩阵元素等基本操作,并最终利用它们来解决实际
问题。
思考与习题
一、填空题
1.一维数组的逻辑结构是( ),存储结构是( );对于二维数组
或多维数组,可分为( )和( )两种不同的存储方式。
2.对于一个二维数组A[m][n],若按以行优先存储,则任一元素aij
相对于a00的地址为( )。
3.三维数组A[c1..d1,c2..d2,c3..d3]共含有( )个元素。
4.数组A[1..10,-2..6,2..8]以行优先的顺序存储,设第一个元素的
首地址是100,每个元素占3个存储单元的存储空间,则元素a507的存
储地址为( )。
二、选择题
1.二维数组M的成员是4个字符(每个字符占一个存储单元)组
成的字符串,行下标i的范围从0到8,列下标j的范围从0到5,则存放
M至少需要()个字节。
A.90
B.360
C.216
D.540
2.二维数组M的元素是4个字符(每个字符占一个存储单元)组成的
字符串,行下标i的范围从0到4,列下标j的范围从0到5,M按行存储时元
素m35的起始地址与M按列存储时元素( )的起始地址相同。
A.m43
B.m34
C.m35
D.m44
3.稀疏矩阵一般的压缩存储方法有两种,即()。
A.二维数组和三维数组 B. 三元组表和散列
C.三元组表和十字链表 D. 散列和十字链表
三、应用题
1.有数组A[4][4],把1到16个整数分别按顺序放入a00…a03,
a10…a13,a20…a23,a30…a33中,编写一个函数获取数据,并求出两条对
角线元素的乘积。
2.试写一个算法,查找十字链表中某一个非零元素x。
3.给定矩阵S如下,写出它的三元组表和十字链表。