您的位置:首页 > 编程语言 > C语言/C++

C++实现矩阵压缩存储与(快速)转置

2016-12-10 19:05 471 查看
  注意:以下所有代码均在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 <iostream>
using namespace std;

template <typename T>
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<<"列"<<endl;
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()
{
if (NULL != _data)
{
delete []_data;
_data = NULL;
}
}
protected:
T* _data;
size_t _n;
};

void TestSymmetricMatrix()
{
int a [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> 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 <iostream>
#include <vector>
using namespace std;

template <typename T>
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 <typename T>
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<T>(i,j,arr[i*n+j]));
}
}
}
}
void PrintSparseMatrix()
{
size_t index = 0;
cout<<"SparseMatrix:"<<_m<<"行"<<_n<<"列"<<endl;
for (size_t i = 0; i < _m; i++)
{
for(size_t j = 0; j < _n; j++)
{
//有效值
if (index < _a.size()
&& _a[index]._row == i
&& _a[index]._col == j)
{
cout<<_a[index]._value<<" ";
index++;
}
else//无效值
{
cout<<_invalid<<" ";
}
}
cout<<endl;
}
cout<<endl;
}

protected:
vector<Triple<T>> _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<int> sp((int*)array,6,5,0);
sp.PrintSparseMatrix();
}
int main()
{
TestSparseMatrix();
system("pause");
return 0;
}
运行结果:



4、稀疏矩阵的转置

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

如:



算法分析



代码实现:

#include <iostream>
#include <vector>
using namespace std;

template <typename T>
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 <typename T>
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<T>(i,j,arr[i*n+j]));
}
}
}
}
void PrintSparseMatrix()
{
size_t index = 0;
cout<<"SparseMatrix:"<<_m<<"行"<<_n<<"列"<<endl;
for (size_t i = 0; i < _m; i++)
{
for(size_t j = 0; j < _n; j++)
{
//有效值
if (index < _a.size()
&& _a[index]._row == i
&& _a[index]._col == j)
{
cout<<_a[index]._value<<" ";
index++;
}
else//无效值
{
cout<<_invalid<<" ";
}
}
cout<<endl;
}
cout<<endl;
}
SparseMatrix<T> Transport()   //矩阵的转置--普通算法
{
SparseMatrix<T> 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<T> tmp(_a[index]._col,_a[index]._row,_a[index]._value);
tsm._a.push_back(tmp);
}
++index;
}
}
return tsm;
}

protected:
vector<Triple<T>> _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<int> sp((int*)array,6,5,0);
sp.PrintSparseMatrix();

SparseMatrix<int> sp1;
sp1 = sp.Transport();
sp1.PrintSparseMatrix();
}
int main()
{
TestSparseMatrix();
system("pause");
return 0;
}
运行结果:



5、稀疏矩阵的快速转置

      上面我们实现了对稀疏矩阵的转置,但是仔细观看代码,你会发现它做了很多无用功,单纯把三元组遍历了col次,那么有没有一种更快速更高效的算法呢?是的,它就是接来来的快速转置。

算法思想

      (1)统计出转置之后的矩阵每一行有效值的个数count,并将有效数据直接定位到新三元组的对应位置处,此时的时间复杂度为O(2*有效值的个数+N)

      (2)用count统计新矩阵每一行的有效值的个数等价于统计原来矩阵的每一列的有效值的个数,通过遍历原来的三元组,将原来三元组的列下标作为count的下标,只要原来三元组的列存在有效值该count的对应下标就+1.

      (3)start找到新矩阵的每一行的第一个元素在新三元组的存储下标.

      (4)此时再次遍历原来的三元组,将该数据直接放入新三元组对应的下标处,此时将start位置的存储数据+1,防止插入到相同的位置抹掉以前存放的数据.

算法分析



代码实现
#include <iostream>
#include <vector>
using namespace std;

template <typename T>
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 <typename T>
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<T>(i,j,arr[i*n+j]));
}
}
}
}
void PrintSparseMatrix()
{
size_t index = 0;
cout<<"SparseMatrix:"<<_m<<"行"<<_n<<"列"<<endl;
for (size_t i = 0; i < _m; i++)
{
for(size_t j = 0; j < _n; j++)
{
//有效值
if (index < _a.size()
&& _a[index]._row == i
&& _a[index]._col == j)
{
cout<<_a[index]._value<<" ";
index++;
}
else//无效值
{
cout<<_invalid<<" ";
}
}
cout<<endl;
}
cout<<endl;
}
SparseMatrix<T> Transport()   //矩阵的转置--普通算法
{
SparseMatrix<T> 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<T> tmp(_a[index]._col,_a[index]._row,_a[index]._value);
tsm._a.push_back(tmp);
}
++index;
}
}
return tsm;
}

SparseMatrix<T> FastTransport()     //矩阵的转置--快速算法
{
SparseMatrix<T> 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<Triple<T>> _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<int> sp((int*)array,6,5,0);
sp.PrintSparseMatrix();

SparseMatrix<int> sp1;
sp1 = sp.Transport();
sp1.PrintSparseMatrix();

SparseMatrix<int> sp2;
sp2 = sp.FastTransport();
sp2.PrintSparseMatrix();
}
int main()
{
TestSparseMatrix();
system("pause");
return 0;
}
运行结果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: