注意:以下所有代码均在VS2010环境下运行测试
了解了C语言以后,我们都知道,要存储一个矩阵,用一个二维数组即可实现,今天,由我来带领大家玩点新鲜的,对矩阵进行压缩存储并对其进行转置。
一、对称矩阵及对称矩阵的压缩存储
1、什么是对称矩阵?
设一个N*N的方阵A,A中任意元素Aij,当且仅当Aij == Aji(0 <= i <= N-1
&& 0 <= j <= N-1),则矩阵A是对称矩阵。以矩阵的对角线为分隔,分为上三
角和下三角。
2、对称矩阵的压缩存储
压缩存储称矩阵存储时只需要存储上三角/下三角的数据,所以最多存
储n(n+1)/2个数据(相当于1+2+...+n,即等差数列求和)。
对称矩阵和压缩存储的对应关系:下三角存储i>=j, SymmetricMatrix[i][j] ==
Array[i*(i+1)/2+j]
3、对称矩阵举例
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
4、代码实现对称矩阵的压缩存储
#include
using namespace std;
template
class SymmetricMatrix
{
public:
SymmetricMatrix(T* arr,size_t n)
:_data(new T[n*(n+1)/2])//已知空间大小为n*(n+1)/2
,_n(n)
{
size_t index = 0;
for (size_t i = 0; i < n; i++)
{
for (size_t j = 0; j < n; j++)
{
if (i >= j)//下三角
{
_data[index] = arr[i*n+j];
index++;
}
else//上三角
{
break;
}
}
}
}
T& Access(size_t i,size_t j)//获取上三角的数据
{
if (i >= j)//下三角元素
{
//由对称矩阵可得i>=j, A[i][j] ==_data[i*(i+1)/2+j]
return _data[i*(i+1)/2+j];
}
else//上三角元素
{
swap(i,j);//行列交换
return _data[i*(i+1)/2+j];
}
}
void PrintSymmetricMatrix()
{
cout<<"SymmetricMatrix:"<<_n<<"行"<<_n<<"列"< sy((int*)a,5);
sy.PrintSymmetricMatrix();
}
int main()
{
TestSymmetricMatrix();
system("pause");
return 0;
}
二、稀疏矩阵及稀疏矩阵的压缩存储
1、什么样的矩阵称为稀疏矩阵?
一个M行N列(即M*N)的矩阵,矩阵中有效值的个数远小于无效值的个数,且这些数据的分布没有规律。
例如:一个6行5列的稀疏矩阵(M = 6,N = 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
2、稀疏矩阵的压缩存储
压缩存储只存储极少数的有效数据。使用{row,col,value}三元组存储每一个有效
数据,三元组按原矩阵中的位置,以行优先级先后顺序依次存放。
3、代码实现稀疏矩阵的压缩存储
#include
#include
using namespace std;
template
struct Triple//三元组
{
Triple(size_t row,size_t col,T& value)
:_row(row)
,_col(col)
,_value(value)
{}
Triple()
{}
size_t _row;
size_t _col;
T _value;
};
template
class SparseMatrix//稀疏矩阵
{
public:
SparseMatrix(T* arr,size_t m,size_t n,const T& invalid)
:_a(NULL)
,_m(m)
,_n(n)
,_invalid(invalid)
{
size_t index = 0;
for (size_t i = 0; i < m; i++)
{
for (size_t j = 0; j < n; j++)
{
if (arr[i*n+j] != invalid)//说明是有效数据
{
_a.push_back(Triple(i,j,arr[i*n+j]));
}
}
}
}
void PrintSparseMatrix()
{
size_t index = 0;
cout<<"SparseMatrix:"<<_m<<"行"<<_n<<"列"<> _a;
size_t _m;//行
size_t _n;//列
T _invalid;//无效值
};
void TestSparseMatrix()
{
int array [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 sp((int*)array,6,5,0);
sp.PrintSparseMatrix();
}
int main()
{
TestSparseMatrix();
system("pause");
return 0;
}
运行结果:
4、稀疏矩阵的转置
将原矩阵的行、列对换,也就是将[i][j]和[j][i]位置上的数据对换。
如:
算法分析:
代码实现:
#include
#include
using namespace std;
template
struct Triple//三元组
{
Triple(size_t row = 0,size_t col = 0,T value = T())
:_row(row)
,_col(col)
,_value(value)
{}
size_t _row;
size_t _col;
T _value;
};
template
class SparseMatrix//稀疏矩阵
{
public:
SparseMatrix()
:_a(NULL)
,_m(0)
,_n(0)
,_invalid(T())
{}
SparseMatrix(T* arr,size_t m,size_t n,const T& invalid)
:_a(NULL)
,_m(m)
,_n(n)
,_invalid(invalid)
{
size_t index = 0;
for (size_t i = 0; i < m; i++)
{
for (size_t j = 0; j < n; j++)
{
if (arr[i*n+j] != invalid)//说明是有效数据
{
_a.push_back(Triple(i,j,arr[i*n+j]));
}
}
}
}
void PrintSparseMatrix()
{
size_t index = 0;
cout<<"SparseMatrix:"<<_m<<"行"<<_n<<"列"< Transport() //矩阵的转置--普通算法
{
SparseMatrix tsm;
tsm._m = _n;
tsm._n = _m;
tsm._a.reserve(_a.size());//开辟有效元素个空间
for (size_t i = 0;i < _n; ++i)
{
//i控制新矩阵的行也就是旧矩阵的列
size_t index=0;
while (index < _a.size())
{
//index控制原来三元组的元素个数
if (_a[index]._col == i)
{
//遍历原来的三元组如果存在满足对应下标的元素则进入新的三元组.
Triple tmp(_a[index]._col,_a[index]._row,_a[index]._value);
tsm._a.push_back(tmp);
}
++index;
}
}
return tsm;
}
protected:
vector> _a;
size_t _m;//行
size_t _n;//列
T _invalid;//无效值
};
void TestSparseMatrix()
{
int array [6][5] =
{{1, 0, 3, 0, 5},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{2, 0, 4, 0, 6},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}};
SparseMatrix sp((int*)array,6,5,0);
sp.PrintSparseMatrix();
SparseMatrix sp1;
sp1 = sp.Transport();
sp1.PrintSparseMatrix();
}
int main()
{
TestSparseMatrix();
system("pause");
return 0;
}
运行结果:
5、稀疏矩阵的快速转置
上面我们实现了对稀疏矩阵的转置,但是仔细观看代码,你会发现它做了很多无用功,单纯把三元组遍历了col次,那么有没有一种更快速更高效的算法呢?是的,它就是接来来的快速转置。
算法思想:
#include
#include
using namespace std;
template
struct Triple//三元组
{
Triple(size_t row = 0,size_t col = 0,T value = T())
:_row(row)
,_col(col)
,_value(value)
{}
size_t _row;
size_t _col;
T _value;
};
template
class SparseMatrix//稀疏矩阵
{
public:
SparseMatrix()
:_a(NULL)
,_m(0)
,_n(0)
,_invalid(T())
{}
SparseMatrix(T* arr,size_t m,size_t n,const T& invalid)
:_a(NULL)
,_m(m)
,_n(n)
,_invalid(invalid)
{
size_t index = 0;
for (size_t i = 0; i < m; i++)
{
for (size_t j = 0; j < n; j++)
{
if (arr[i*n+j] != invalid)//说明是有效数据
{
_a.push_back(Triple(i,j,arr[i*n+j]));
}
}
}
}
void PrintSparseMatrix()
{
size_t index = 0;
cout<<"SparseMatrix:"<<_m<<"行"<<_n<<"列"< Transport() //矩阵的转置--普通算法
{
SparseMatrix tsm;
tsm._m = _n;
tsm._n = _m;
tsm._a.reserve(_a.size());//开辟有效元素个空间
for (size_t i = 0;i < _n; ++i)
{
//i控制新矩阵的行也就是旧矩阵的列
size_t index=0;
while (index < _a.size())
{
//index控制原来三元组的元素个数
if (_a[index]._col == i)
{
//遍历原来的三元组如果存在满足对应下标的元素则进入新的三元组.
Triple tmp(_a[index]._col,_a[index]._row,_a[index]._value);
tsm._a.push_back(tmp);
}
++index;
}
}
return tsm;
}
SparseMatrix FastTransport() //矩阵的转置--快速算法
{
SparseMatrix ftsm;
ftsm._m = _n; //新矩阵的行就是旧矩阵的列
ftsm._n = _m;
ftsm._a.resize(_a.size());
//count统计新矩阵每一行有效值的个数
int *count = new int[_n];
memset(count,0,sizeof(int)*_n); //将count空间初值赋值为0
for (size_t i = 0; i < _a.size(); ++i)//记录每列有效元素的个数
{
int col = _a[i]._col;
++count[col];
}
int *start = new int[_n]; //start记录新矩阵的每行第一个元素在三元组的存储下标
memset(start,0,sizeof(int)*_n);//将start空间初值赋值为0
size_t i = 0;
start[i] = 0;//第0列的下标为0
for (i = 1; i < _n; ++i)
{
//每列的初始坐标为上一列初始坐标+上一列有效元素的个数
start[i] = start[i-1] + count[i-1];
}
//遍历原来的三元组,找到数据就直接放入新三元组的对应的下标处
for (size_t i = 0; i < _a.size(); ++i)
{
int col = _a[i]._col;
size_t tmp = start[col];
ftsm._a[tmp]._row = _a[i]._col;
ftsm._a[tmp]._col = _a[i]._row;
ftsm._a[tmp]._value = _a[i]._value;
++start[col]; //防止同一行的有效数据放入相同位置
}
delete[]start;
delete[]count;
return ftsm;
}
protected:
vector> _a;
size_t _m;//行
size_t _n;//列
T _invalid;//无效值
};
void TestSparseMatrix()
{
int array [6][5] =
{{1, 0, 3, 0, 5},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0},
{2, 0, 4, 0, 6},
{0, 0, 0, 0, 0},
{0, 0, 0, 0, 0}};
SparseMatrix sp((int*)array,6,5,0);
sp.PrintSparseMatrix();
SparseMatrix sp1;
sp1 = sp.Transport();
sp1.PrintSparseMatrix();
SparseMatrix sp2;
sp2 = sp.FastTransport();
sp2.PrintSparseMatrix();
}
int main()
{
TestSparseMatrix();
system("pause");
return 0;
}
运行结果: