稀疏矩阵的定义:
M*N的矩阵,矩阵中有效值的个数远小于无效值的个数,且这些数据的分布没有规律。
具体实现代码:
1、定义一个三元组
因为要分布没有规律,所以用一个三元组来保存坐标与值之间的关系
代码如下:
template<class T>
struct Triple
{
public:
Triple(int row=0,int col=0,const T& value=T())
:_row(row)
, _col(col)
, _value(value)
{}
public:
int _row;
int _col;
T _value;
};
2、存储稀疏矩阵的数组
主要包括构造、打印、转置、快速转置
(1)函数声明如下:
template<class T>
class SparseMatrix
{
public:
SparseMatrix(T* a, int m, int n, const T& invalid);//非法值
SparseMatrix();
public:
void Display();//打印
SparseMatrix<T> Transport();//转置
SparseMatrix<T> FastTransport();//快速转置
protected:
vector<Triple<T>> _a;
size_t _rows;
size_t _columns;
T _invalid;
};
(2)构造和打印:
template<class T>
SparseMatrix<T>::SparseMatrix(T* a, int m, int n, const T& invalid)//非法值
:_rows(m)
, _columns(n)
, _invalid(invalid)
{
for (int i = 0; i < m; ++i)
{
for (int j = 0; j < n; ++j)
{
if (a[i*n + j] != invalid)
{
/* Triple<T> t(i, j, a[i*n + j]);
_a.push_back(t);*/
//或者写成下面这样
_a.push_back(Triple<T>(i, j, a[i*n + j]));
}
}
}
}
template<class T>
SparseMatrix<T>::SparseMatrix()
:_columns(0)
, _rows(0)
, _invalid(0)
{}
template<class T>
void SparseMatrix<T>::Display()//打印
{
size_t index = 0;
for (int i = 0; i < _rows; ++i)
{
for (int j = 0; j < _columns; ++j)
{
if (index < _a.size()
&& _a[index]._row == i
&&_a[index]._col == j)
{
cout << _a[index++]._value << " ";
}
else
{
cout << _invalid << " ";
}
}
cout << endl;
}
cout << endl;
}
(3)转置:
将原矩阵的行、列对换,也就是将[i][j]和[j][i]位置上的数据对换。
例子:
实际上是一列一列的遍历矩阵,对应的压缩存储的数组,则是跳着分别去找对应列为1、2、3....的元素,每个列数相同的元素都找完,然后列数加1,再去遍历此列数的元素,元素是一个一个放入数组中的
代码如下:
template<class T>
SparseMatrix<T> SparseMatrix<T>::Transport()//转置
{
SparseMatrix<T> tmp;
tmp._invalid = _invalid;
tmp._columns = _rows;
tmp._rows = _columns;
tmp._a.reserve(_a.size());
for (size_t i = 0; i < _columns; ++i)
{
size_t index = 0;
while (index < _a.size())
{
if (_a[index]._col == i)
{
Triple<T> tp;
tp._row = _a[index]._col;
tp._col = _a[index]._row;
tp._value = _a[index]._value;
tmp._a.push_back(tp);
}
++index;
}
}
return tmp;
}
(4)快速转置:
其实是跳着插入,前面普通转置的方法每一次每一次都要去找列数相同的元素时都要去遍历一边数组,而快速转置则是遍历一边数组,每一次都找到其元素所在转置矩阵中的具体位置,但是有前提的要先事先遍历一边数组,统计出每一列元素的个数,并且计算出每一列开头元素开始的位置。然后就可以实现精确定位,不过要事先开辟好数组的空间,不能一个一个的push_back了
例子:
代码如下:
template<class T>
SparseMatrix<T> SparseMatrix<T>::FastTransport()//快速转置
{
SparseMatrix<T> tmp;
tmp._invalid = _invalid;
tmp._columns = _rows;
tmp._rows = _columns;
//tmp._a.reserve(_a.size());
tmp._a.resize(_a.size());
int* rowCount = new int[_columns];
int* rowStarts = new int[_columns];
memset(rowCount, 0, sizeof(int)*_columns);
memset(rowStarts, 0, sizeof(int)*_columns);
size_t index = 0;
while (index < _a.size())
{
rowCount[_a[index]._col]++;
index++;
}
rowStarts[0] = 0;
for (size_t i = 1; i < _columns; ++i)
{
rowStarts[i] = rowStarts[i - 1] + rowCount[i - 1];
}
index = 0;
while (index < _a.size())
{
int& rowStart = rowStarts[_a[index]._col];//一定要加引用,后面才可以++
Triple<T> tp;
tp._row = _a[index]._col;
tp._col = _a[index]._row;
tp._value = _a[index]._value;
tmp._a[rowStart++] = tp;
++index;
}
delete[] rowStarts;
delete[] rowCount;
return tmp;
}
测试用例:
void SparseMatrixTest()
{
int a[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> _sa((int*)a, 6, 5, 0);
cout << endl;
_sa.Display();
/*SparseMatrix<int> sm1 = _sa.Transport();
sm1.Display();*/
SparseMatrix<int> sm2 = _sa.FastTransport();
sm2.Display();
}