稀疏矩阵的存储方式及其快速转置的实现



稀疏矩阵:

M*N的矩阵,矩阵中有效值的个数远小于无效值的个数,且这些数据的分布没有规律


如下图矩阵:

稀疏矩阵的压缩存储方式:

压缩存储极少数的有效数据 。使用{row,col,value}三元组存储每一个有效数据,三元组按原矩阵中的位置,以行优先级先后顺序依次存放(以便转置打印矩阵)。

稀疏矩阵的转置:

(1)将存储的有效数据行优先存储改为按列优先存储(转置后的矩阵行变为列,列变为行)
(2)将有效数据的行列交换

稀疏矩阵的快速转置:

(1)用一个数组保存转置后的矩阵每行有效元素的个数。       例如上述矩阵: RowCount[6]={3,0,0,3,0,0}
(2)另一个数组保存转置后的矩阵每行第一个有效元素在压缩存储中的下标位置 。例如上述矩阵 RowStart[6]={0,3,3,3,6,6}
(每行有效元素的起始位置是上一行有效元素的起始位置+上一行有效元素的个数)
(3)根据压缩存储的稀疏矩阵,及RowStart[]设置转置后的矩阵的压缩存储


实现代码:

#pragma once
#include<iostream>
#include<vector>
using namespace std;

template<class T>
struct Triple//三元组结构
{
	Triple(const T& value=T(), size_t row=0, size_t col=0)//三元组默认构造函数
	:_value(value)
	, _row(row)
	, _col(col)
	{}
	T _value;
	size_t _row;
	size_t _col;
};
template<class T>
class SparseMatrix
{
public:
	SparseMatrix(size_t m, size_t n, const T& invaild)//重载稀疏矩阵构造函数
		:_RowSize(n)
		, _ColSize(m)
		, _invaild(invaild)
	{}
	SparseMatrix(const T* a, size_t m, size_t n, const T& invaild)//稀疏矩阵构造函数,压缩存储
	:_RowSize(m)
	, _ColSize(n)
	, _invaild(invaild)
	{
		int index = 0;
		for (size_t i = 0; i < m; i++)//行优先存储
		{
			for (size_t j = 0; j < n; j++)//遍历矩阵
			{
				if (a[i*n + j] != _invaild)//若该矩阵元素是有效值
				{
					_a.push_back(Triple<T>(a[i*n + j],i,j));//存储元素
				}
			}
		}
	}
	void display()//打印矩阵
	{
		int index = 0;//vetcor元素下标
		for (size_t i = 0; i < _RowSize; i++)
		{
			for (size_t j = 0; j < _ColSize; j++)
			{
				if (index != _a.size() && _a[index]._row == i && _a[index]._col == j)
					//若三元组中有该位置元素(元素行列都相等,该位置元素是有效值)
				{
					cout << _a[index++]._value << " ";//打印该元素
				}
				else
					cout << _invaild << " ";//若该位置元素不是有效值,则打印非有效值
			}
			cout << endl;
		}
	}
	SparseMatrix<T> Transport()//矩阵转置
	{
		int index = 0;
		SparseMatrix<T> tmp(_RowSize, _ColSize, _invaild);//定义一个转置后的矩阵的大小
		for (size_t j = 0; j < _RowSize; j++) //
		{
			for (size_t i = 0; i < _a.size(); i++)//遍历vector
			{
				if (_a[i]._col == j)//若
				{
					tmp._a.push_back(Triple<T>(_a[i]._value, _a[i]._col, _a[i]._row));
					index++;
				}
			}

		}
		return tmp;
	}
	SparseMatrix<T> FastTransport()
	{
		int index = 0;
		SparseMatrix<T> tmp(_RowSize, _ColSize, _invaild);
		int *RowCount=new int[tmp._RowSize];//每行有效元素的个数
		int *RowStart=new int[tmp._RowSize];//每行有效元素在tmp._a中的起始位置
		memset(RowCount, 0, sizeof(int)*tmp._RowSize);//初始化
		memset(RowStart, 0, sizeof(int)*tmp._RowSize);

		while (index != _a.size())//设置数组RowCount的数据
		{
			RowCount[_a[index]._col]++;
			index++;
		}

		index = 1;
		RowStart[0] = 0;//第一个有效数字起始位置为0
		while (index < tmp._RowSize)//设置数组RowSize的数据
		{
			RowStart[index] = RowStart[index - 1] + RowCount[index - 1];
			//每行有效元素的个数是上一行有效元素的起始位置+上一行有效元素的个数
			index++;
		}
		
		//压缩存储转置后的矩阵
		index = 0;
		tmp._a.resize(_a.size());//将tmp._a的大小置为矩阵中有效元素的个数
		while (index < _a.size())
		{
			Triple<T> t(_a[index]._value, _a[index]._col, _a[index]._row);
			//定义临时三元组,该三元组的行是原来矩阵的列,列是原来矩阵的行,值不变


			tmp._a[RowStart[_a[index]._col]++] = t;
			index++;
            
			//(1) _a[index]._col 取出_a中的元素的行
			//(2)RowStart[_a[index]._col] 该元素的行作为RowStart的下标(_a中元素的列是tmp._a中元素的行)
			//得到的值是该元素在tmp._a中的存储位置
			//RowStart[_a[index]._col]++  表示若一行有多个元素,那么依次存储在该行有效元素的位置的后面
			
		}
		return tmp;
	}
protected:
	vector<Triple<T> > _a;//用容器存储三元组元素
	size_t _RowSize;//矩阵行数
	size_t _ColSize;//矩阵列数
    T _invaild;//非有效值
};

<span style="font-size:14px;">
</span>

测试代码:

#include "SparseMatrix.hpp"
void TestSparseMatrix()
{
	int a[6][5] = {
		{1,0,3,0,5},
		{0,0,0,0,0},
		{0,0,0,0,0},
		{1,0,3,0,5},
		{0,0,0,0,0},
		{0,0,0,0,0}
	};
	SparseMatrix<int> Matrix((int *)a, 5, 5, 0);
	Matrix.display();
	cout << "---------" << endl;
	SparseMatrix<int> Matrix1 =Matrix.Transport();
	Matrix1.display();
	cout << "---------" << endl;
	SparseMatrix<int> Matrix2 = Matrix.FastTransport();
	Matrix2.display();
}
int main()
{
	TestSparseMatrix();
	getchar();
	return 0;
}

运行结果:


表述的可能不是很清楚,如果有疑问,可以联系我或者在本文后添加评论提出疑问,我会及时回复。。







  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值