稀疏矩阵的压缩存储和转置

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

2、稀疏矩阵的压缩存储:压缩存储值存储极少数的有效数据。  

    由于非零元素分布没有任何规律,所以在进行压缩存储的时侯需要存储无效值的同时还要存储有效元素在矩阵中的位置,即有效元素所在的行号和列号,也就是在存储某个元素比如aij的值的同时,还需要存储该元素所在的行号i和它的列号j,这样就构成了一个三元组(i,j,aij)的线性表。

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

3、稀疏矩阵的转置:将原矩阵的行、列对换,也就是将[i][j]和[j][i]位置上的数据对换。

wKiom1cR-cfyo2U3AABMvBLxNz0305.png

具体实现如下:

#include<iostream>
using namespace std;
#include<assert.h>
#include<vector>//vector是一个能够存放任意类型的动态数组,能够增加和压缩数据,也认为是容器
template<class T>
struct Triple//三元组结构体
{
	int _row;
	int _col;
	T _value;//非法值/无效值
	Triple()
		:_row(0)
		, _col(0)
		, _value(0)
	{}
	Triple(int row, int col, T& value)
		:_row(row)
		, _col(col)
		, _value(value)
	{}
};
template<class T>
class SparseMatrix
{
public:
	SparseMatrix();
	SparseMatrix(T* array, int n, int m, const T& invalid);
	~SparseMatrix();
	void Display();
	SparseMatrix<T> Transpose();//转置
	SparseMatrix<T> FastTranspose();//快速转置
private:
	vector<Triple<T>> _array;
	size_t _rows;
	size_t _cols;
	T _invalid;
};
template<class T>
SparseMatrix<T>::SparseMatrix()
:_rows(0)
, _cols(0)
, _invalid(0)
{}
template<class T>
SparseMatrix<T>::~SparseMatrix()
{}
template<class T>
SparseMatrix<T>::SparseMatrix(T* array, int m, int n, const T& invalid)
:_rows(m)
, _cols(n)
, _invalid(invalid)
{//由于数组定义必须知道列数,则以行优先级存放稀疏矩阵,压缩存储值存储极少数的有效数据
	assert(array);
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			if (array[n*i + j] != invalid)
			{
				_array.push_back(Triple<T>(i, j, array[n*i + j]));//vector中push_back插入数据
			}
		}
	}
}
template<class T>
void SparseMatrix<T>::Display()
{
	//使用下标访问元素
	size_t indx = 0;
	for (size_t i = 0; i < _rows; i++)
	{
		for (size_t j = 0; j < _cols; j++)
		{//如果_array[indx]的行列等于i和j,且indx在范围内就打印其有效值,否则打印无效值0
			if (indx < _array.size() && _array[indx]._row == i && _array[indx]._col == j)
			{
				cout << _array[indx]._value << " ";
				indx++;
			}
			else
			{
				cout << _invalid << " ";
			}
		}
		cout << endl;
	}
	cout << endl;
	使用迭代器访问所有有效元素
	//vector<Triple<T>>::iterator it;
	//for (it = _array.begin(); it != _array.end(); it++)
	//{
	//	cout << "row:" << (*it)._row << " col:" << (*it)._col << endl;
	//	cout << "_value:" << (*it)._value << endl;
	//}
	//cout << endl;
}
template<class T>
//时间复杂度为:O(_cols*有效数据个数),空间复杂度:O(1)
SparseMatrix<T> SparseMatrix<T>::Transpose()//转置
{//原矩阵中元素以行优先存储,转置后的矩阵是以原矩阵的列优先存储的
	size_t indx;
	SparseMatrix<T> tmp;//新建一个稀疏矩阵存放交换后的行列和有效数据
	tmp._invalid = _invalid;
	tmp._rows = _cols;
	tmp._cols = _rows;
	tmp._array.reserve(_array.size());//reserve()扩容到size
	for (size_t i = 0; i < _cols; i++)//以列进行查找,进行行列交换
	{
		indx = 0;//将列数为i的有效元素进行行列交换
		while (indx < _array.size())
		{
			if (i == _array[indx]._col)
			{
				//Triple<T> point;
				//point._row = _array[indx]._col;
				//point._col = _array[indx]._row;
				//point._value = _array[indx]._value;
				//tmp._array.push_back(point);
				tmp._array.push_back(Triple<T>(i, _array[indx]._row, _array[indx]._value));
			}
			indx++;
		}
	}
	return tmp;
}
template<class T>
//时间复杂度为:O(_cols+有效数据个数),空间复杂度:O(_cols)
SparseMatrix<T> SparseMatrix<T>::FastTranspose()//快速转置
{
	SparseMatrix<T> tmp;//新建一个稀疏矩阵存放交换后的行列和有效数据
	tmp._invalid = _invalid;
	tmp._rows = _cols;
	tmp._cols = _rows;
	tmp._array.resize(_array.size());//reserve()只扩容到,但不一定能用此空间,resize()开辟size个空间
	//rowCounts统计转置后的矩阵每一行的数据个数(原矩阵的每列的)
	//rowStarts统计转置后的矩阵每一行在压缩矩阵中存储的开始位置
	int* rowCounts = new int[_cols];
	int* rowStarts = new int[_cols];
	memset(rowCounts, 0, sizeof(int)*_cols);//memset()按字节初始化为某值(0)
	memset(rowStarts, 0, sizeof(int)*_cols);
	size_t indx = 0;
	while (indx < _array.size())
	{
		rowCounts[_array[indx]._col]++;//利用下标统计每列数据个数,2 0 2 0 2 
		indx++;
	}
	rowStarts[0] = 0;//记录开始位置
	for (size_t i = 1; i < _cols; i++)
	{//转置的矩阵每一行在压缩矩阵中存储的开始位置,0 2 2 4 4
		rowStarts[i] = rowCounts[i - 1] + rowStarts[i - 1];
	}
	indx = 0;
	while (indx < _array.size())//快速定位
	{//rowStarts存放转置后每一行的开始位置,rowStart不断更新同行数据位置,转置后同一行数据的位置不断++,故用&
		int& rowStart = rowStarts[_array[indx]._col];
		tmp._array[rowStart++] = Triple<T>(_array[indx]._col, _array[indx]._row, _array[indx]._value);
		indx++;
	}
	return tmp;
}

测试用例如下:

void Test2()
{
	int a[][5] = {
		{ 5, 0, 3, 0, 1 },
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
		{ 6, 0, 4, 0, 2 },
		{ 0, 0, 0, 0, 0 },
		{ 0, 0, 0, 0, 0 },
	};
	SparseMatrix<int> s1((int*)a, 6, 5, 0);
	s1.Display();
	SparseMatrix<int> s2;
	s2 = s1.Transpose();
	s2.Display();
	SparseMatrix<int> s3;
	s3 = s1.FastTranspose();
	s3.Display();
}

对称矩阵及对称矩阵的压缩存储

设一个N*M的方阵A,A中任意元素Aij,当且仅当Aij == Aji(0<=i<=N-1&&0<=j<=N-1),则矩阵A是对称矩阵。以矩阵的对角线为分隔,分为上三角和下三角。

压缩存储称矩阵存储时只需要存储上三角/下三角的数据,所以最多存储n(n+1)/2个数据。

对称矩阵和压缩存储的对应关系:下三角存储i>=j, SymmetricMatrix[i][j]==Array[i*(i+1)/2+j]

下面对下三角的压缩存储的具体实现:

template<class T>
class SymmetricMatrix
{
public:
	SymmetricMatrix()
		:_a(NULL)
		, _size(0)
	{}
	SymmetricMatrix(T* a, const size_t size)
		:_a(new T[size*(size+1)/2])
		, _size(size*(size+1)/2)
	{
		assert(a);
		//size_t indx = 0;
		for (size_t i = 0; i < size; i++)
		{
			for (size_t j = 0; j < size; j++)
			{
				if (i >= j)//if (i >= j && indx < _size)
				{
					//方法一:_a[indx] = a[i*size + j];indx++;
					//方法二:运用下三角矩阵的对称矩阵和压缩存储的对应关系:
					//下三角存储i>=j,  SymmetricMatrix[i][j] == Array[i*(i+1)/2+j]
					_a[i*(i + 1) / 2 + j] = a[i*size + j];
				}
			}
		}
	}
	void Display()
	{
		if (_a)
		{
			for (size_t indx = 0; indx < _size; indx++)
			{
				cout << _a[indx] << " ";
			}
			cout << "\nsize = " << _size << endl;
		}
	}
	~SymmetricMatrix()
	{
		if (_a)
		{
			delete[] _a;
		}
	}
private:
	T* _a;
	size_t _size;
};
void Test1()
{
	int a[][5] = {
		{ 0, 1, 2, 3, 4 },
		{ 1, 0, 1, 2, 3 },
		{ 2, 1, 0, 1, 2 },
		{ 3, 2, 1, 0, 1 }, 
		{ 4, 3, 2, 1, 0 },
	};
	SymmetricMatrix<int> s1((int*)a, 5);
	s1.Display();
}

本文出自 “Scen” 博客,请务必保留此出处http://10741357.blog.51cto.com/10731357/1764516

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
稀疏矩阵压缩存储方式是一种常用的优化存储方式,可以有效节省存储空间。在这种存储方式,矩阵的非零元素被存储为一个三元组 (i, j, value) 的形式,其 i 和 j 分别表示该元素在矩阵的行坐标和列坐标,value 表示该元素的值。 转置操作是指将矩阵的行和列交换,即行变为列,列变为行。在稀疏矩阵压缩存储方式转置操作需要重新生成一个新的三元组数组来存储转置后的矩阵。 以下是稀疏矩阵压缩存储转置算法的详细解释。 1. 定义一个三元组结构体来存储稀疏矩阵的三元组信息: ``` typedef struct { int row; // 行坐标 int col; // 列坐标 int value; // 元素值 } Triple; ``` 2. 定义一个稀疏矩阵结构体来存储稀疏矩阵的基本信息,包括矩阵的行数、列数、非零元素个数和三元组数组: ``` typedef struct { int rows; // 矩阵的行数 int cols; // 矩阵的列数 int nnz; // 矩阵的非零元素个数 Triple *triples; // 矩阵的三元组数组 } SparseMatrix; ``` 3. 定义一个稀疏矩阵转置的函数,该函数接受一个稀疏矩阵作为参数,并返回转置后的稀疏矩阵: ``` SparseMatrix transpose(SparseMatrix A) { SparseMatrix B; B.rows = A.cols; B.cols = A.rows; B.nnz = A.nnz; B.triples = (Triple *)malloc(B.nnz * sizeof(Triple)); int *rowCounts = (int *)calloc(A.cols, sizeof(int)); for (int i = 0; i < A.nnz; i++) { rowCounts[A.triples[i].col]++; } int *rowOffsets = (int *)calloc(A.cols + 1, sizeof(int)); rowOffsets[0] = 0; for (int i = 1; i <= A.cols; i++) { rowOffsets[i] = rowOffsets[i - 1] + rowCounts[i - 1]; } for (int i = 0; i < A.nnz; i++) { int j = A.triples[i].col; int index = rowOffsets[j]; B.triples[index].row = A.triples[i].col; B.triples[index].col = A.triples[i].row; B.triples[index].value = A.triples[i].value; rowOffsets[j]++; } free(rowCounts); free(rowOffsets); return B; } ``` 4. 在转置函数,首先定义一个新的稀疏矩阵 B,该矩阵的行数等于 A 的列数,列数等于 A 的行数,非零元素个数等于 A 的非零元素个数。 5. 然后,定义两个辅助数组 rowCounts 和 rowOffsets,用于计算转置后的矩阵的三元组数组的索引。 6. 对于 rowCounts 数组,它的长度为 A 的列数,每个元素表示该列的非零元素个数。遍历 A 的三元组数组,在 rowCounts 数组对应的列上加 1。 7. 对于 rowOffsets 数组,它的长度为 A 的列数加 1,每个元素表示转置后的矩阵的三元组数组该列的起始索引。遍历 rowCounts 数组,累计计算 rowOffsets 数组每个元素的值。 8. 遍历 A 的三元组数组,根据 rowOffsets 数组的值,将转置后的三元组存储到 B 的三元组数组。 9. 最后,释放 rowCounts 和 rowOffsets 数组,并返回转置后的稀疏矩阵 B。 以上就是稀疏矩阵压缩存储转置算法的详细解释。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值