您的位置:首页 > 理论基础 > 数据结构算法

5.3矩阵的压缩存储(稀疏矩阵转置和快速转置)

2017-02-09 15:22 495 查看
在矩阵中有许多值相同的元素或者是零元素。有时为了节省存储空间,可以对这类矩阵进行压缩存储。所谓的压缩存储是指:为多个值相同的元值分配一个存储空间;对零元不分配空间。

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<M.tu;++p).这个就是我刚刚在上表说的那个意思。这里有个++cpot[col]这是个关键

现在来解释下++cpot[col]:

我们可以看到上表中cpot[col]只有1,3,5,7,8而2,4,5没有,所以用了这个++cpot[col]后他就把每一列的第一个元素移到了第二个。

如果还有同学不懂,下面我给出全部的代码。
不懂的同学单步调试下。

#include <stdio.h>
#include <windows.h>
#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;
}


运行结果如下:



和下面这图是不是一模一样

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息