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

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


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;
}

运行结果如下:


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




©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页