在矩阵层次上,有几个矩阵时很特殊的。这篇文章是主要讲述对称矩阵的压缩存储、访问和还原的。
首先,何为对称矩阵?
首先,对称矩阵是一个二维数组,也是一个方阵。它的行列对应的列列行的数据相等,即arr[i][j]==arr[j][i];如下:
那么我们在存储时还存储一个5*5的的二维数组是不是有点浪费空间了。既然存在arr[i][j]==arr[j][i]这个规律,何不妨只存储一个下三角或者一个上三角的元素呢?即:
存储时将对称矩阵的一半数据存储即为压缩存储。
具体的首先逻辑为:我们申请一个一段连续的空间,即一个一维数组。然后用这个一维数组存储这个对称的元素,并且包括对称轴。
很明显的一个规律,对称轴为(0,0),(1,1),(2,2),(3,3),(4,4),即每一行的元素我们存储到对称轴就可以放弃存储这行后面的元素。且我们发现总元素的个数为N*(N+1)/2,这里的N为矩阵的阶数。为了方便访问。在传参时将二位数组转为一维数组使用,则每一个元素的下标为:i*N+j,i为第i行,j为第j列。
int idx = 0;
_pData = new T[N * (N + 1) / 2];
for (size_t i = 0; i < _row; i++)
{
for (size_t j = 0; j <= i; j++)
_pData[idx++] = a[i * N + j];
}
访问函数:
存储在空间中时是以一维数组保存的,且只保存了一半的元素,那么访问时该如何访问任一元素呢?
因为arr[i][j]==arr[j][i],且如果保存下三角的话,上三角的元素的航和列就是对应下三角的行和列的交换(注:上三角的列>行,下三角的元素行<列),例如:访问arr[3][1],我们只需要返回arr[1][3]的值就行。这很简单,那么问题来了,如何使二维数组的下标和一维数组的下标对应起来呢?
一开始,我们使用压缩存储时的方法为开辟:N*(N+1)/2个大小的空间,所以N*(N+1)/2表示前N行的下三角一个储存了多少个元素,所以第N+1行的元素的下表为N*(N+1)/2+i,i为第N+1行的列数。即:
左侧第一行为当前二维数组存储一维数组中的下标的起始值,在访问行中每一个元素时,只需加上对应的列的下标就行。
T& Acess(int row, int col)
{
if (col > row)
std::swap(row, col);
return _pData[row * (row + 1) / 2 + col];
}
const T& Acess(int row, int col)const
{
if (col > row)
std::swap(row, col);
return _pData[row * (row + 1) / 2 + col];
}
还原输出对称矩阵就简单了,直接调用Acess函数就ok。
friend ostream& operator<<(ostream& os, SymmetricMatrix& sm)
{
for (size_t i = 0; i < sm._row; ++i)
{
for (size_t j = 0; j < sm._col; j++)
os <<sm.Acess(i, j) << " ";
os << endl;
}
return os;
}
关于对称矩阵的完整的实现如下:
#include <iostream>
using namespace std;
template<class T>
class SymmetricMatrix
{
public:
SymmetricMatrix(int* a, size_t N)
: _row(N)
, _col(N)
{
int idx = 0;
_pData = new T[N * (N + 1) / 2];
for (size_t i = 0; i < _row; i++)
{
for (size_t j = 0; j <= i; j++)
_pData[idx++] = a[i * N + j];
}
}
T& Acess(int row, int col)
{
if (col > row)
std::swap(row, col);
return _pData[row * (row + 1) / 2 + col];
}
const T& Acess(int row, int col)const
{
if (col > row)
std::swap(row, col);
return _pData[row * (row + 1) / 2 + col];
}
~SymmetricMatrix()
{
if (_pData)
delete[] _pData;
}
friend ostream& operator<<(ostream& os, SymmetricMatrix& sm)
{
for (size_t i = 0; i < sm._row; ++i)
{
for (size_t j = 0; j < sm._col; j++)
os <<sm.Acess(i, j) << " ";
os << endl;
}
return os;
}
private:
T* _pData;
size_t _row;
size_t _col;
};
void FunTest()
{
int arr[][5]={
/* 0 1 2 3 4*/
/*0*/{ 0, 1, 2, 3, 4 },
/*1*/{ 1, 0, 1, 2, 3 },
/*2*/{ 2, 1, 0, 1, 2 },
/*3*/{ 3, 2, 1, 0, 1 },
/*4*/{ 4, 3, 2, 1, 0 }
};
SymmetricMatrix<int> sm((int*)arr,sizeof(arr)/sizeof(arr[0]));
cout << sm.Acess(4, 2) << endl;
cout << sm.Acess(1, 4) << endl;
cout << sm.Acess(2, 3) << endl;
cout << sm << endl;
}
int main()
{
FunTest();
system("pause");
return 0;
}