多维数组与矩阵
一种比特殊的数据结构。
知识点
多维数组和运算
定义
1、一维数组
是一种元素个数固定的线性表。
2、多维数组
是一种复杂的数据结构,可以看成是线性表的推广,一个n维数组可视为其数据元素为n-1维数组的线性表。
顺序存储
二维数组可有两种存储方法:一种是以行序为主序的存储方式,另一种是以列序为主序的存储方式。在C语言中,采用以行为主序存储。一般二维数组不采取链式存储
二维存储位置计算
对于C语言的二维数组A[m][n],下标从0开始,假设一个数组元素占d个存储单元,则二维数组中任一元素a[i][j]的存储位置可由下式确定:求这个元素之前存了多少元素
loc(A[i][j])=loc(A[0][0])+(i *n + j) * d
会推导,明白思想,不要记。
三维数组存储位置
三维数组A[m][n][p],下标从0开始,假设一个数组元素占d个存储单元,则三维数组中任一元素a[i][j][k]的存储位置可由下式确定:
loc(A[i][j][k])=loc(A[0][0][0]+(i *n *p+ j*p+k) * d
数组转换置算法实现
对于一个m×n的矩阵A,其转置矩阵是一个n×m的矩阵B,而且B[i][j]=A[j][i],0≤i≤n-1,0≤j≤m-1。假设m=5,,n=8。
void trsmat(int a[][8], int b[][5], int m, int n)
{
int i, j;
for (j = O; j < m; j++)
for (i = 0; i < n; i++)
b[i][j] = a[j][i];
}
另一种方法:
一维数组实现
矩阵的压缩存储
特殊矩阵:
是相同值的元素或者零元素在矩阵中的分布有一定规律的矩阵。
对称矩阵
若n阶方阵A中的元素满足下述性质:
aij=aji (0≤i,j≤n-1)
则称A为n阶的对称矩阵。
对于一个n阶对称矩阵,可只存储其下三角矩阵:如下图所示:
以行序为主序存储其下三角(包括对角线)中的元素,将元素压缩存储到1+2+3+…+n=n(n+1)/2个元素的存储空间中,数组M[k]和aij的对应关系:如下图:
三角矩阵
主对角线上/下方均为常数c或零
三角矩阵可压缩存储到数组M[n(n+1)/2+1]中。
下三角矩阵中,以行序为主序存放,与对称矩阵类是,M[k]和aij的对应关系是:
上三角矩阵中,主对角线上的第i行有n-i+1个元素,以行序为主序存放,M[k]和aij的对应关系是:
稀疏矩阵
含有大量的零元素且零元素分布没有规律矩阵称为稀疏矩阵。
三元组表来存储稀疏
每个三元组元素对应稀疏矩阵中的一个非零元素,包含有该元素的行号、列号和元素值。每个三元组元素在线性表中是按照行号值的升序为主序、列号值的升序为辅序(即行号值相同再按列号值顺序)排列的。
三元组数据结构定义:
#define Maxsize 1000 //假设非零元素个数的最大为1000个
typedef struct {
int i,j; //非零元素的行号、列号(下标)
DataType v; //非零元素值
} TriTupleNode;
typedef struct {
TriTupleNode data[Maxsize];//存储三元组的数组
int m,n,t; //矩阵的行数、列数和非零元素个数
} TSMatrix; //稀疏矩阵类型
实例
实现以三元组表结构存储的稀疏矩阵的转置运算。所谓的转置就是行与列互换。
一般转置算法
对M中的每一列col(0≤col≤a.n-1)从头至尾依次扫描三元组表,找出所有列号等col的那些三元组,并将它们的行号和列号互换后再依次存入b->data中,这样就可得到T的按行优先的三元组表。
void TransMatrix(TSMatrix a, TSMatrix *b)
{
//a和*b是矩阵M、T的三元组表表示,求稀疏矩阵M的转置T
int p, q, col;
b->m = a.n;
b->n = a.m; //M和T行列数互换
b->t = a.t; //赋值非零元素个数
if (b->t <= 0)
printf("M中无非零元素!");
else {
q = 0;
for (col = 0; col < a.n; ++col)
for (p = 0; p < a.t; ++p) //扫描M的三元组表
if (a.data[p].j == col) { //找与col相等的三元组
b->data[q].i = a.data[p].j;
b->data[q].j = a.data[p].i;
b->data[q].v = a.data[p].v;
++q;
}
}
}
该算法仅适用于非零元素个数t远远小于矩阵元素个数m×n的情况。因为算法的时间复杂度为O(n×t),即与稀疏矩阵M的列数和非零元素个数的乘积成正比,相对来说是比较大的
快速转置算法
创建两个数组num和rownext。num[j]存放矩阵第j列上非零元素个数,rownext[i]代表转置矩阵第i行的下一个非零元素在b中的位置。
void FaStTran(TSMatfix a, TSMatrix *b)
{
int col, p, t, q;
int *num, *rownext;
num = (int *)calloc(a.n + 1, 4); // 分配n+1个长度为4的连续空间
rownext = (int *)calloc(a.m + 1, 4); // 分配m+1个长度为4的连续空间
b->m = a.n;
b->n = a.m;
b->t = a.t;
if (b->t) {
for (col = 0; col < a.n; ++col)
num[col] = 0; //初始化
for (t = 0; t < a.t; ++t)
++num[a.data[t].j]; //计算每列非零元素数
rownext[0] = 0;
for (col = 1; col < a.n; ++col) //给出b中每一行的起始点
rownext[col] = rownext[col - 1] + num[col - 1];
for (p = 0; p < a.t; ++p) { //执行转置操作
col = a.data[p].j;
q = rownext[col];
b->data[q].i = a.data[p].j;
b->data[q].j = a.data[p].i;
b->data[q].v = a.data[p].v;
++rownext[col]; //下一次再有该行元素,起始点就比上一个加了1
}
}
}
算法的时间复杂度为O(t)
带行表的三元组表
又称为行逻辑链接的顺序表。在按行优先存储的三元组表中,增加一个存储每一行的第一个非零元素在三元组表中位置的数组。
数据结构描述:
typedef struct
{
TriTupleNode data[MaxSize];
int RowTab[MaxRow]; //每行第一个非零元素的位置表
int m,n,t;
}RLSMatrix;
特点:
① 对于任给行号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为矩阵的非零元总数)
注意:
若在行表中令RowTab[m]=t(要求MaxRow>m)会更方便些,且t可省略。