特殊矩阵的存储
对称矩阵
在这里给出一组对称矩阵
因为其对称性,我们不需要存储其全部内容,只需要存储其下三角矩阵或者上三角矩阵。
在这里我选择存储下三角矩阵。
设矩阵的行数为M.
则需要存储的元素个数为 M(M+1)/2,以行优先进行存储。
当对此矩阵进行访问时,我们在内存中存储的数据应该是
当我们要访问第3行第2列(即i>=j)的元素时,便可以通过对其在存储的数据中的偏移量进行读取,偏移量即为该元素行之前的下三角的大小加上该元素所在列号。
当i
template<class T, int N>
class Matrix
{
public:
Matrix(int arr[N][N]) //保存对称矩阵
{
int i = 0;
int j = 0;
int k = 0;
_array.resize(N*(N + 1) / 2);
for (i = 0; i < N; i++) //行
{
for (j = 0; j <= i; j++) //列
{
_array[k++] = arr[i][j];
}
}
}
void Print() //打印
{
int i = 0;
int j = 0;
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
if (i >= j)
printf("%d ", _array[(i*(i + 1)) / 2 + j]);
else
printf("%d ", _array[j*(j + 1) / 2 + i]);
}
cout << endl;
}
}
template<class T, int N>
friend ostream& operator <<(ostream &out, Matrix<T, N>&max) //输出运算符重载
{
int i = 0;
int j = 0;
for (i = 0; i < N; i++) //行
{
for (j = 0; j < N; j++) //列
{
if (i >= j)
printf("%d ", max._array[(i*(i + 1)) / 2 + j]);
else
printf("%d ", max._array[j*(j + 1) / 2 + i]);
}
cout << endl;
}
return out;
}
T& Access(int row, int col) {//访问矩阵
if (col > row)
swap(col, row);
return _array[row*(row + 1) / 2 + col];
}
private:
vector<T> _array;
};
int main()
{
int arr[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 } }; // 0 1 2 3 4 0 1 2 3 0 1 2 0 1 0
Matrix<int, 5> max(arr);
cout << max.Access(3, 2) << endl;
cout << max.Access(2, 3) << endl;
//cout << max << endl;
max.Print();
system("pause");
return 0;
}
测试结果
稀疏矩阵
稀疏矩阵压缩存储
这里给出一组稀疏矩阵
因为其有效元素远远小于矩阵总元素,所以只需要保存其有效元素的位置信息与数据内容
在这里用一个结构体来保存有效元素的信息。
template<class T>
struct Trituple //有效节点
{
Trituple()
{}
Trituple(int row, int col, T data)
:_row(row)
, _col(col)
, _data(data)
{}
int _row;
int _col;
T _data;
};
实现
template<class T, int M, int N>
class SparseMatrix
{
public:
template<class T>
struct Trituple //有效节点
{
Trituple()
{}
Trituple(int row, int col, T data)
:_row(row)
, _col(col)
, _data(data)
{}
int _row;
int _col;
T _data;
};
SparseMatrix()
{}
SparseMatrix(int M, int N, T invalid)
:_row(M)
, _col(N)
, _invalid(invalid)
{}
SparseMatrix(T arr[M][N],T invalid) //保存稀疏矩阵
:_row(M)
,_col(N)
,_invalid(invalid){
int i = 0;
int j = 0;
for (i = 0; i < M; i++){
for (j = 0; j < N; j++){
if (arr[i][j] != _invalid){
Trituple<T> t(i,j,arr[i][j]);
_v.push_back(t); //如果不是无效值 则存入
}
}
}
}
void Print(){ //打印
int i = 0;
int j = 0;
size_t index = 0;
for (i = 0; i < _row; i++){
for (j = 0; j < _col; j++){
if (index < _v.size()){
if (_v[index]._row == i&&_v[index]._col == j) {
cout << _v[index]._data << " ";
index++;
}
else
cout << _invalid << " ";
}
else
cout << _invalid << " ";
}
cout << endl;
}
}
template<class T,int M,int N>
friend ostream& operator<<(ostream &out, SparseMatrix<T, M, N>&matrix)
{
int i = 0;
int j = 0;
size_t index = 0;
for (i = 0; i < matrix._row; i++) {
for (j = 0; j <matrix._col; j++) {
if (index <matrix._v.size()) {
if (matrix._v[index]._row == i&&matrix._v[index]._col == j) {
cout << matrix._v[index]._data << " ";
index++;
}
else
cout << matrix._invalid << " ";
}
else
cout << matrix._invalid << " ";
}
cout << endl;
}
return out;
}
public:
vector<Trituple<T>> _v; //保存有效节点
int _row;
int _col;
T _invalid;
};
矩阵的逆置
将原矩阵的行和列进行交换,也就是将[i][j]交换为[j][i].
但是如果只是将有效节点表中的行和列交换会造成,因为首次存储时行优先的存储顺序造成结果无法完整输出。
此时若依然按照行优先顺序进行输出,则不能全部输出。
此时可以先依据行号进行排序,再将所有元素的行列进行交换。
(因为首次存储时一招行优先进行存储,所以排序交换后的元素也将是按照行优先的顺序排列的)
实现:
void InverseMatrix(){ //逆置矩阵
size_t i = 0;
size_t j = 0;
size_t index = 0;
for (index = 0; index < _v.size(); index++){ //先将里面的坐标交换
swap(_v[index]._row,_v[index]._col);
}
swap(_row, _col);
for (i = 0; i < _v.size(); i++){ //排序
for (j = i; j < _v.size() - 1; j++){
if (_v[j]._row > _v[j + 1]._row){
swap(_v[j], _v[j + 1]);
}
}
}
稀疏矩阵快速逆置
快速逆置
用两个数组 分别统计
1.转置后的矩阵每一行的参数个数
2.转置后矩阵每行元素在压缩矩阵中存储的开始位置
过程
实现
SparseMatrix& QuickInverseMatrix(){ //快速逆置
int j = 0;
int i = 0;
size_t index = 0;
int *RowCount = new int[N];
memset(RowCount, 0, N*sizeof(int));
for (; index < _v.size(); index++) { //转置后的矩阵每一行的参数个数
RowCount[_v[index]._col]++;
} // 2 0 2 0 2
int *RowStart = new int[N];
memset(RowStart, 0, N * sizeof(int)); //转置后矩阵每行在压缩矩阵中存储的开始位置
for (i = 0; i < N; i++){
if (i == 0) // 第一行元素的偏移量永远为0
RowStart[i] = 0;
else {
RowStart[i] = RowCount[i - 1] + RowStart[i - 1];
}
} //0 2 2 4 4
// 打印
index = 0;
SparseMatrix *sm=new SparseMatrix;
sm->_row = _col;
sm->_col = _row;
sm->_v.resize(_v.size());
for (size_t i = 0; i < _v.size();i++) {
int &num = RowStart[_v[i]._col];
sm->_v[num] = _v[i];
swap(sm->_v[num]._row, sm->_v[num]._col);
num++;
}
delete[]RowCount;
delete[]RowStart;
return *sm;
}
矩阵的加法
矩阵的简单运算—–加法
将两个矩阵中相同位置的元素进行加法运算(两个矩阵的行和列相同)。
因为我们存储时只存储有效元素,所以不能通过简单遍历的方式进行运算。
在用二维数组存储矩阵的时候,因为二维数组也是一个线性结构,所以可以根据有效元素在矩阵中的偏移量进行比较,如果偏移量不同,则将偏移量较小的元素放入,如果相同则进行加法运算。
实现
SparseMatrix& operator+(SparseMatrix&t) //矩阵加法
{
if (_col != t._col || _row != t._row)
{
exit(EXIT_FAILURE);
}
SparseMatrix*sm = new SparseMatrix(_row, _col, _invalid);
size_t index1 = 0;
size_t index2 = 0;
//利用偏移量比较先后顺序
while (index1<_v.size()&&index2<t._v.size())
{
if ((_v[index1]._row*_col+_v[index1]._col) < (t._v[index2]._row*_col + t._v[index2]._col))
{//_v在前
sm->_v.push_back(_v[index1]);
index1++;
}
else if ((_v[index1]._row*_col + _v[index1]._col) > (t._v[index2]._row*_col + t._v[index2]._col))
{//t._v 在前
sm->_v.push_back(t._v[index2]);
index2++;
}
else
{ //偏移量相同
//Trituple *t = new Trituple(_row, _col, _v[index1]._data + t._v[index2]._data);
Trituple<T> t1(_v[index1]._row,_v[index1]._col, _v[index1]._data + t._v[index2]._data);
sm->_v.push_back(t1);
index1++;
index2++;
}
}
while (index1 < _v.size()) {
sm->_v.push_back(_v[index1]);
index1++;
}
while (index2 < t._v.size()) {
sm->_v.push_back(t._v[index2]);
index2++;
}
return *sm;
}