在矩阵中有许多值相同的元素或者是零元素。有时为了节省存储空间,可以对这类矩阵进行压缩存储。所谓的压缩存储是指:为多个值相同的元值分配一个存储空间;对零元不分配空间。
5.32稀疏矩阵
在m*n的矩阵中,有t个元素不为零。零α=t/m*n,称 α为矩阵的稀疏因子。通常认为α<=0.05时称为稀疏矩阵。
对于稀疏矩阵的非零元我们有下面这个表示:
如:(,(1,2,12),(1,3,9),(3,1,-3),(3,6,14),(4,3,24),(5,2,18),(6,1,15),(6,4,-7))
如下图所示:
下面我们来看下三元组的结构体:
下面是书中代码(严蔚敏版的数据结构)
#define MAXSIZE 12500 //非零元素个数的最大值为12500
typedef struct{
int i, j; //该非零元的行下标和列下标
ElemType e;
}Triple;
typedef struct{
Triple data[MAXSIZE + 1]; //非零元三元组表,data[0]未用
int mu, nu, tu; //矩阵的行数、列数和非零元个数
}TSMatrix;
分析下:
学过线性代数的都知道,转置运算是一种最简单的矩阵运算。这里的思路和我们在链表上看到的有相似之处,结点变成了非零元素,线性表编程了非零元三元组的表。但多出了矩阵的行数,列数和非零元个数
对应一个m*n的矩阵M它的转置矩阵是T,如下图所示:
P·S:所谓的置换就是行和列进行交换,也就是关于主对角线对称(矩阵左上角到右下角的线称为主对角线)
而在程序里面,他将会是下面这张图这样:
这里的i对应行,j对应列,v表示值。
从分析a和b之间的差异可见只要做到:
1.将矩阵的行列值相互转换。
2.将每个三元组中的i和j交换。
3.重排三元组之间的次序便可实现矩阵的转置。
下面是书中给我们提供的伪代码:
Status TransposeSMatrix(TSMatrix M, TSMatrix &T)
{
// 采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T
int p, q, col;
T.mu = M.nu; T.nu = M.mu; T.tu = M.tu;
if (T.tu)
{
q = 1;
for (col = 1; col <= M.nu; ++col)
for (p = 1; p <= M.tu; ++p)
if (M.data[p].j == 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;
++q;
}
}
return Ok;
} // TransposeSMatrix
下面来分析下:
下面是矩阵的快速转置方法:col表示列,从M矩阵的第一列开始。比如col=1时,他先检索M的第一列,把非零元中第一列的换成T中的第一行,就这个思路。
原理是:如果能预先确定矩阵M中每一列(即T中每一行)的第一个非零元在b.data中(上面那图是b.data)恰当位置。那么在对a.data中的三元组一次做转置时,便可直接放到b.data中恰当的位置上去。
设两个向量:num和cpot
num[col]表示矩阵M中第col列中的非零元素个数。
cpot[col]指M中第col列的第一个非零元在b.data中的恰当位置。
有下面两个公式:
cpot[1]=1;
cpot[col]=copt[col-1]+num[col-1] 2<=col<=a.nu
图如下:
下面来分析下这个表:
下面是书中代码:cpot[1]=1.
cpot[2]=num[1]+cpot[1]=1+2=3
cpot[3]=num[2]+cpot[2]=2+3=5
cpot[4]=num[3]+cpot[3]=2+5=7
cpot[5]=num[4]+cpot[4]=1+7=8
cpot[6]=num[5]+cpot[5]=0+8=8
cpot[7]=num[6]+cpot[6]=1+8=9
这个表就是这么填的,但是在代码里面就不一样了。我这里先提一下,代码里面有覆盖和范围这种概念,这是什么意思,意思就是,大家看cpot[5]和cpot[6]都是8,那么在最后,他只会保留cpot[6],而cpot[7]他这里是9,但本身就只有8个元素,哪来第九个呢?所以这个cpot[9]在程序里面是没有用的。
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T)
{
// 采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T
int col, t, p, q;
int num[20], cpot[20];
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) // 求 M 中每一列所含非零元的个数
++num[M.data[t].j];
cpot[1] = 1;
// 求 M 中每一列的第一个非零元在 b.data 中的序号
for (col = 2; col <= M.nu; ++col)
cpot[col] = cpot[col - 1] + num[col - 1];
for (p = 1; p <= M.tu; ++p)
{
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];
} // for
} // if
return OK;
} // FastTransposeSMatrix
分析如下:
这个程序的关键就是他只用了一个for循环,而上面那个程序用了两个for循环,这使得时间复杂度降低了。这个for(p=1;p
现在来解释下++cpot[col]:
我们可以看到上表中cpot[col]只有1,3,5,7,8而2,4,5没有,所以用了这个++cpot[col]后他就把每一列的第一个元素移到了第二个。
不懂的同学单步调试下。
#include
#include
#define MAXSIZE 1250
#define OK 1
#define ERROR 0
#define TRUE 1
#define FLASE 0
typedef int Status;
typedef int ElemType;
typedef struct{
int i, j; //该非零元的行下标和列下标
ElemType e; //非零元对应的值
}Triple;
typedef struct{
Triple data[MAXSIZE + 1]; //非零元三元组表,data[0]未用
int mu, nu, tu; //矩阵的行数,列数,非零元个数
}TSMatrix;
Status FastTransposeSMatrix(TSMatrix M, TSMatrix &T) //快速转置
{ //采用三元组顺序表存储表示,求稀疏矩阵M的转置矩阵T
T.mu = M.nu;
T.nu = M.mu;
T.tu = M.tu;
if (T.tu)
{
int col;
int num[100], cpot[100];
for (col = 1; col <= M.nu; ++col)
num[col] = 0; //num数组的初始化
for (int t = 1; t <= M.tu; ++t)
++num[M.data[t].j]; //求M中每一列含有的非零元个数
cpot[1] = 1;
for (col = 2; col <= M.nu; ++col)
cpot[col] = cpot[col - 1] + num[col - 1]; //求cpot向量
int q;
for (int p = 1; p <= M.tu; ++p)
{
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];
}//for
}//if
return OK;
}//FastTransposeSMatrix
Status main()
{
TSMatrix M;
TSMatrix T;
printf("请输入原矩阵:\n");
printf("行数、列数: ");
scanf_s("%d%d", &M.mu, &M.nu);
printf("元素总数: ");
scanf_s("%d", &M.tu);
printf("输入各个对应压缩值:\n");
for (int i = 1; i <= M.tu; ++i)
scanf_s("%d%d%d", &M.data[i].i, &M.data[i].j, &M.data[i].e);
FastTransposeSMatrix(M, T);
printf("转置后行数、列数、元素总数非别为:\n%d %d %d\n\n", T.mu, T.nu, T.tu);
printf("值为:\n");
for (int t = 1; t <= T.tu; ++t)
printf("%d %d %d\n", T.data[t].i, T.data[t].j, T.data[t].e);
system("pause");
return OK;
}
和下面这图是不是一模一样