多维数组
(2013-02-23 15:38:26)分类: 数据结构 |
简介
数组和广义表也是一种常用的数据结构,是线性表的推广。大多数的程序设计语言都提供数组来描述数据,其他数据结构的顺序存储结构多是以数组形式来描述的。广义表在文本处理、人工智能和计算机图形学等领域得到广泛应用,并且其使用价值和应用效果逐渐受到人们重视。在LISP和PROLOG程序中,所有的概念和对象都是用广义表表示的。
数组的概念
一、定义
数组是由下标与值组成的数偶的有序集合,即它的每一个元素是由一个值与一组下标所确定。
说明:
(1)对于每组有定义的下标总有一个相应的数值与之对应,并且这些值都是同一类型的。
(2)下标决定了元素的位置,数组中各元素之间的关系由下标体现出来,下标的个数决定了数组的维数。
(3)因为由下标所决定的位置之间的关系可以看成是一种有序的线性关系,因此以说数组是有限个相同类型数据元素组成的有序序列。从这个角度看,数组是线性表的推广,它的逻辑结构实际是一种线性结构。
二、一维数组
一维数组[a0,a1,...,an-1]由n个元素组成,每个元素除具有相同类型的值外,还有一个下标以确定元素的位置,显然一维数组是一个线性表。
一维数组在计算机内是存放在一块连续的存储单元中,适合于随机查找。
三、二维数组
二维数组可以看成是向量的推广。二维数组由m×n个元素组成,元素之间是有规则的排列。每个元素由值及一对能确定元素位置的下标组成。
例如,设A是一个有m行n列的二维数组,则A可以表示为:
(a0 0,a0 1,...,a0 n-1,a1 0,a1 1,...,a1 n-1,...,am-1 0,
am-1 2, ...,am-1)
同理,按数组的列关系的描述,aij的直接后继是ai+1j,对应的线性表为: (a0 0,al 0,...,am-10,a01,a11,...,am-11,..., 也可以将二维数组A看成是由m个行向量[X0,X1, …,Xm-1]T组成。
其中,Xi=( ai0, ai1, ….,ain-1), 0≤i≤m-1;也可以将二维数组A看成是由n个列向量[y0, y1, ……,yn-1]组成,其中 yi=(a0i, a1i, …..,am-1i), 0≤i≤n-1。因此,二维数组也可以看成是有m个(或n个)元素的特殊线性表,其元素为一维数组。
四、多维数组
对于n维数组,每个元素由值及n个能确定元素位置的下标组成,按数组的n个下标变化次序关系的描述,可以确定数组元素的前驱和后继关系并写出对应的线性表。
n维数组也可以由元素为(n一1)维数组的特殊线性表来定义,这样维数大于一的多维数组是由线性表结构辗转合成得到的,是线性表的推广。
对于数组,通常只有两种操作:
· (1)给定一组下标,存取相应的数据元素;
· (2)给定一组下标,修改相应数据元素中的某一个或某几个数据项的值。
数组的顺序存储结构
由于数组一般不作插入或删除操作,也就是说,一旦建立了数组,则结构中的数据元素个数和元素之间的关系就不再发生变动。因此,采用顺序存储结构表示数组是自然的事了。
一、一维数组顺序存储结构
一维数组a[t]是由元素a[0],a[1],...,a[t-l]组成的有限序列,若数组的每个元素占s个存储单元,并且从地址a开始依次分配数组各元素,则分配情况为:
矩阵是科学与工程计算问题中常用的数学对象之一。
若用LOC(a[i])来表示数组的第i个元素的存储位置,则 .
LOC(a[i])=LOC(a[0])+i*s=a+i*s
(0
二、二维数组顺序存储结构
二维数组顺序存储有两种方式:一种是以行序为主序,另一种是以列序为主序。
1.以行序为主序进行存储分配的方法
首先存储行号为0的n个元素,对于这n个元素按列号从小到大依次存储:紧接着存储行号为1的n个元素…最后存储行号为m-1的n个元素。
地址计算
LOC(a[i][j])=LOC(a[0][0])+(i*n+j)*s=a+(i*n+j)*s
2.以列序为主序进行存储分配的方法
首先存储列号为0的m个元素,对于这m个元素按行号从小到大依次存储:紧接着存储列号为1的m个元素…最后存储列号为n-1的m个元素。
地址计算
LOC(a[i][j])=LOC(a[0][0])+(j*m+i)*s=a+(j*m+i)*s
多维数组的顺序存储
1.存放规则
以上规则可以推广到多维数组的情况:行优先顺序也称为低下标优先或左边下标优先于右边下标。具体实现时,按行号从小到大的顺序,先将第一行中元素全部存放好,再存放第二行元素,第三行元素,依次类推 ……
在BASIC语言、 PASCAL语言、 C/C++语言等高级语言程序设计中,都是按行优先顺序存放的。
列优先顺序也称为高下标优先或右边下标优先于左边下标。具体实现时,按列号从小到大的顺序,先将第一列中元素全部存放好,再存放第二列元素,第三列元素,依次类推 ……
在FORTRAN语言程序设计中,数组是按列优先顺序存放的。
按上述两种方式顺序存储的序组,只要知道开始结点的存放地址(即基地址),维数和每维的上、下界,以及每个数组元素所占用的单元数,就可以将数组元素的存放地址表示为其下标的线性函数。因此,数组中的任一元素可以在相同的时间内存取,即顺序存储的数组是一个随机存取结构。
2.地址计算
三维数组Am×n×p按行优先存放的地址计算公式为:LOC(aijk)=LOC(a000)+(i×n×p+j×p+k)×s
三维数组Am×n×p按列优先存放的地址计算公式为:
LOC(aijk)=LOC(a000)+(k×m×n+j×m+i)×s
特殊矩阵
1.对称矩阵
(1)对称矩阵
则称A为对称矩阵。
【例】下图便是一个5阶对称矩阵。
(2)对称矩阵的压缩存储
①按"行优先顺序"存储主对角线(包括对角线)以下的元素
②元素aij的存放位置
③aij和sa[k]之间的对应关系:
(3)对称矩阵的地址计算公式
2、三角矩阵
(1)三角矩阵的划分
①上三角矩阵
②下三角矩阵
(2)三角矩阵的压缩存储
① 上三角矩阵中aij和sa[k]之间的对应关系
所以:
②下三角矩阵中aij和sa[k]之间的对应关系
3.对角矩阵
【例】下图给出了一个三对角矩阵。
稀疏矩阵
1、稀疏矩阵的压缩存储
2、三元组表
【例】下图(a)所示的稀疏矩阵A的三元组表表示见图(b)
(1)三元组表的类型说明
(2) 压缩存储结构上矩阵的转置运算
【例】下图中的B和上图中的A互为转置矩阵。
①三元组表表示的矩阵转置的思想方法
第一步:根据A矩阵的行数、列数和非零元总数确定B矩阵的列数、行数和非零元总数。
第二步:当三元组表非空(A矩阵的非零元不为0)时,根据A矩阵三元组表的结点空间data(以下简称为三元组表),将A的三元组表a->data置换为B的三元组表b->data。
②三元组表的转置
③具体算法:
void TransMatrix(TriTupleTable *b,TriTupleTable *a)
{//*a,*b是矩阵A、B的三元组表表示,求A转置为B
④算法分析
该算法的时间主要耗费在col和p的二重循环上:
通常用二维数组表示矩阵时,其转置算法的执行时间是O(m×n),它正比于行数和列数的乘积。
3、带行表的三元组表
(1)类型描述
(2)带行表的三元组表的操作
① 对于任给行号i(0≤i≤m-1),能迅速地确定该行的第一个非零元在三元组表中的存储位置为RowTab[i]
② RowTab[i](0≤i≤m-1)表示第i行之前的所有行的非零元数。
③ 第i行上的非零元数目为RowTab[i+1]-RowTab[i](0≤i≤m-2)
④ 最后一行(即第m-l行)的非零元数目为t-RowTab[m-1](t为矩阵的非零元总数)
4.稀疏矩阵压缩存储方式分析
(1) 三元组表和带行表的三元组表的特点
【例】执行将矩阵B相加到矩阵A上的运算时,某位置上的结果可能会由非零元变为零元,但也可能由零元变为非零元,这就会引起在A的三元组表中进行删除和插人操作,从而导致大量结点的移动。对此类运算采用链式存储结构为宜。
(2)稀疏矩阵的链式结构
稀疏矩阵的十字链表表示
当稀疏矩阵中非零元的位置或个数经常变动时,三元组就不适合于作稀疏矩阵的存储结构,此时,采用链表作为存储结构更为恰当。
十字链表的构成
1、非零元素的结点结构
十字链表为稀疏矩阵中的链接存储中的一种较好的存储方法,在该方法中,每一个非零元用一个结点表示,结点中除了表示非零元所在的行(row)、列(col)和值(val)的域外,还需增加两个链域:行指针域(right),用来指向本行中下一个非零元素;列指针域(down) ,用来指向本列中下一个非零元素。
2.表头结点结构
对于矩阵中每一行,分别设置一个行链表表头结点。为处理方便,使表头结点和非零元素结点结构相同,其中row和col域的值均为∞,right域指向该行非零元素的第一个结点,next(与val域共用结构中相同空间)域指向下一行的表头结点。
对于矩阵中每一列,分别设置一个列链表表头结点。dowm域指向该列非零元素的第一个结点,next域指向下一列的表头结点,其他域同行链表表头结点。
3.结点结构的定义
typedef struct node
{int row,col;
union {int val; //非零元素结点用val域
struct node *next;//表头结点用next域
}tag;
struct node *right,*down;
}NODE
4. 行链表和列链表
稀疏矩阵中同一行的非零元通过向右的right指针链接成一个带表头结点的循环链表。同一列的非零元也通过down指针链接成一个带表头结点的循链链表。因此,每个非零元既是第i行循环链表中的一个结点,又是第j列循环链表中的一个结点,相当于处在一个十字交叉路口,故称链表为十字链表。
5.表头结点的共享
在行(列)表头结点中,行表头结点只用了right域,列表头结点只用了down域,故两组表头结点可以共用,即第i行链表和第i列链表共用一个表头结点。
6.表头链表
所有表头结点本身又可以按其行号(或列号)增长的顺序通过next域链接成一个循环链表。另外,再增加一个附加结点(由指针hm指示,行、列域分别为稀疏矩阵的行、列数目),附加结点指向第一个表头结点,则整个十字链表可由hm指针唯一确定。
如图:
稀疏矩阵的相加运算
当稀疏矩阵用三元组表进行相加时,有可能出现非零元素的位置变动,这时候,不宜采用三元组表作存储结构,而应该采用十字链表较方便。