对称矩阵的压缩存储与稀疏矩阵的转置
一、对称矩阵与对称矩阵的压缩存储
对称矩阵(Symmetric Matrix)
平时存储一般矩阵我们都是用的二维数组,但我们可以看出对称矩阵上三角与下三角的内容是一样的。为了节省空间我们采取压缩存储的方式,将对称矩阵的上三角或者下三角的数据存储在一个一维数组里面。如果对称矩阵为n*n的矩阵,我们可以知道压缩存储的一维数组的大小为n*(n+1)/2。
现在我们用模板类来实现对称矩阵的压缩存储(下面以存储下三角为例):
#include<iostream>
using namespace std;
using namespace std;
template<class T>
class SymmetricMatrix
{
public:
SymmetricMatrix(T *a, size_t N)
:_N(N)
{
_matrix = new T[N*(N + 1) / 2];
size_t index = 0;
for (size_t i = 0; i < N; i++)
{
for (size_t j = 0; j < N; j++)
{
if (i >= j)
{
_matrix[index++] = a[i*N+j];//依次将下三角的数据存入一位数组中
}
}
}
}
void PrintCompressedMatrix()//打印一维数组
{
for (size_t i = 0; i < _N*(_N + 1) / 2; i++)
{
cout << _matrix[i] << " ";
}
cout << endl;
}
void PrintSymmetricMatrix()//根据存储的一维数组打印原对称矩阵
{
for (size_t i = 0; i < _N; i++)
{
for (size_t j = 0; j < _N; j++)
{
cout << _Access(i, j) << " ";
}
cout << endl;
}
cout << endl;
}
~SymmetricMatrix()
{
delete[] _matrix;
_matrix = NULL;
}
protected:
T& _Access(size_t i, size_t j)
{
if (i < j)
{
swap(i, j);
}
return _matrix[i*(i + 1) / 2 + j];
}
protected:
T *_matrix;//对称矩阵存储的一维数组
size_t _N;
};
void Test()
{
int array[5][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> SM((int*)array, 5);
SM.PrintSymmetricMatrix();
SM.PrintCompressedMatrix();
}
二、稀疏矩阵及稀疏矩阵的压缩存储(Sparse Matrix)
稀疏矩阵中大多数据都为0,而有效数据则很少。若是直接采用二维数组来存储稀疏矩阵太浪费存储空间,而且在运算中也会花费大量时间来计算无效数据0。因此我们利用三元组(Triple)来记录有效数据的坐标及其值,然后将三元组存储在一个一维数组中。
2、稀疏矩阵的普通转置
根据稀疏矩阵压缩存储的一位数组进行转置形成新的一维数组。将原稀疏矩阵按列遍历,并按顺序将有效数据放入转置后新的一维数组中。如下图中,先在_matrix中找到1和2,然后将其依次放入tmp._matrix中,以此类推。
3、稀疏矩阵的快速转置
从普通转置方法中我们可以发现稀疏矩阵中有多少有效数据,我们就需要遍历多少次_matrix,这样会比较浪费空间。那有没有更快的方法呢?下面我们来介绍稀疏矩阵的快速转置。
我们定义两个新的一维数组rowcount和rowpos,其中rowcount用来记录新矩阵每一行(即原矩阵每一列)有效数据的个数,rowpos用来记录转置后新矩阵每一行第一个有效数据在新一维数组中的位置(下标),且每次将数据放入新数组之后,相应的rowpos加1。
#include<iostream>
using namespace std;
#include<vector>
template<class T>
struct Triple//三元组结构,用来记录有效数据的坐标及其值
{
size_t _row;
size_t _col;
T _value;
Triple(size_t row, size_t col, const T& value)
:_row(row)
, _col(col)
, _value(value)
{}
};
template<class T>
class SparseMatrix
{
public:
SparseMatrix(T *a, size_t M, size_t N, const T& invalid = T())//稀疏矩阵的压缩存储
:_M(M)
, _N(N)
, _invalid(invalid)
{
for (size_t i = 0; i < M; i++)
{
for (size_t j = 0; j < N; j++)
{
if (a[i*N + j] != invalid)
{
Triple<T> t(i, j, a[i*N + j]);
_matrix.push_back(t);
}
}
}
}
void PrintCompressedMatrix()//打印一维数组
{
size_t index = 0;
while (index<_matrix.size())
{
cout << _matrix[index++]._value << " ";
}
cout << endl;
}
void PrintSparseMatrix()//根据一维数组打印稀疏矩阵
{
size_t index = 0;
for (size_t i = 0; i < _M; i++)
{
for (size_t j = 0; j < _N; j++)
{
if (index < _matrix.size()
&& i == _matrix[index]._row
&&j == _matrix[index]._col)
{
cout << _matrix[index++]._value << " ";
}
else
cout << _invalid << " ";
}
cout << endl;
}
cout << endl;
}
SparseMatrix<T> TransposeMatrix(T *a)//稀疏矩阵的普通转置
{
SparseMatrix<T> tmp(a, _M, _N, _invalid);//定义一个新数组存放转置后矩阵的数据
size_t count = 0;
tmp._M = _N;
tmp._N = _M;
//将原一维数组中的数据放入新一维数组中相应的位置,并将行列互换
for (size_t i = 0; i < _N; i++)
{
for (size_t index = 0; index < _matrix.size(); index++)
{
if (_matrix[index]._col == i)
{
tmp._matrix[count]._row = _matrix[index]._col;
tmp._matrix[count]._col = _matrix[index]._row;
tmp._matrix[count]._value = _matrix[index]._value;
count++;
}
}
}
return tmp;
}
SparseMatrix<T> FastTransposeMatrix(T *a)//稀疏矩阵的快速转置
{
SparseMatrix<T> tmp(a, _M, _N, _invalid);
tmp._M = _N;
tmp._N = _M;
tmp._matrix.resize(_matrix.size());
int *rowcount = new int[_N];//记录转置后的新矩阵每行有效数据的个数
memset(rowcount, 0, sizeof(int)*_N);
for (size_t i = 0; i < _matrix.size(); i++)
{
size_t col = _matrix[i]._col;
++rowcount[col];
}
int *rowpos = new int[_N];//记录转置后新矩阵每一行第一个有效数据在三元组中的位置(下标)
memset(rowpos, 0, sizeof(int)*_N);
size_t i = 0;
rowpos[i] = 0;
for (i = 1; i < _N; i++)
{
rowpos[i] = rowcount[i - 1] + rowpos[i - 1];
}
for (size_t i = 0; i < _matrix.size(); i++)
{
size_t col = _matrix[i]._col;
size_t index = rowpos[col];
tmp._matrix[index]._col = _matrix[i]._row;
tmp._matrix[index]._row = _matrix[i]._col;
tmp._matrix[index]._value = _matrix[i]._value;
++rowpos[col];
}
delete[] rowcount;
delete[] rowpos;
return tmp;
}
~SparseMatrix()
{}
protected:
vector<Triple<T>> _matrix;//使用vector不用手动开辟空间
size_t _M;
size_t _N;
T _invalid;//非法值,这里指0
};
void Test()
{
int array[5][6] =
{
{ 1, 0, 3, 0, 0, 5 },
{ 0, 0, 0, 0, 0, 0 },
{ 2, 0, 0, 4, 0, 0 },
{ 0, 0, 0, 0, 0, 0 },
{ 0, 0, 6, 0, 0, 0 },
};
SparseMatrix<int> sp((int*)array, 5, 6, 0);
sp.PrintSparseMatrix();
SparseMatrix<int> sm = sp.TransposeMatrix((int*)array);
sm.PrintSparseMatrix();
sm.PrintCompressedMatrix();
SparseMatrix<int> SM = sp.FastTransposeMatrix((int*)array);
SM.PrintSparseMatrix();
SM.PrintCompressedMatrix();
}