稀疏矩阵的访问、普通逆置和快速逆置、还原输出以及加法
来源:互联网 发布:ubuntu 卸载node 编辑:程序博客网 时间:2024/06/10 12:35
首先稀疏矩阵的概念:
在一个矩阵中(并不要求为方阵)无效元素的个数远远大于有效元素的个数,我们称之为——稀疏矩阵。一般没有一个明确的界限分开稀疏矩阵和普通矩阵,不过一些人认为:有效元素的个数/无效元素的个数<0.05即可称之为稀疏矩阵。
跟对称矩阵压缩存储的逻辑相似:我们只需存储稀疏矩阵的有限元素就行,但有效元素的位置并没有什么规律,所以在存储时还要存储相应元素的行、列的下标。
可以用结构体实现:
template<class T> struct Trituple { Trituple(size_t row, size_t col, const T& data) : _row(row) , _col(col) , _data(data) {} Trituple() {} size_t _row; size_t _col; T _data; };
则类中稀疏矩阵的成员应为:
private: vector<Trituple<T>> _pData; size_t _row; size_t _col; T _invalid;//无效元素的值
构造函数的实现很简单,我们只需要遍历一下这个矩阵,将不为无效值的元素的值、行和列存储就行。这里采用的方法也是讲二位数组转为一维数组进行访问。
SparseMatrix(int* array, size_t row, size_t col, const T& invalid) : _row(row) , _col(col) , _invalid(invalid) { for (size_t i = 0; i < _row; i++) { for (size_t j = 0; j < _col; j++) { if (array[i*_col + j] != _invalid) _pData.push_back(Trituple<T>(i, j, array[i*_col + j])); } } }
稀疏矩阵的访问:
只需判断需要访问的行、列在压缩保存的一维数组中有没有对应的元素,如果有,则输出该值;如果没有,则输出无效值。
T& Access(int row, int col) { for (size_t i = 0; i < _pData.size();++i) { if (_pData[i]._row == row&&_pData[i]._col == col) return _pData[i]._data; } return _invalid; }
稀疏矩阵的还原输出:
同样,我们只需判断要访问的位置是否存在有效元素,有则输出,无则输出无效元素。此时我们最好不要直接访问Acess()函数,这样的话开销太大,时间复杂度过高,不是一个优秀的代码。所以我们使用vector中的迭代器访问被压缩存储的每一个元素。或者每次进入之后判断行、列的关系。
template<class T> friend ostream& operator<<(ostream& os, SparseMatrix<T>& s) { size_t index = 0; for (size_t i = 0; i < s._row; ++i) { for (size_t j = 0; j < s._col; ++j) { if ((index < s._pData.size()) && (s._pData[index]._row == i) && (s._pData[index]._col == j)) //注意:index<s._pData.size()是为了index访问不越界。 os << setw(3) << s._pData[index++]._data << " "; else os << setw(3) << s._invalid << " "; } os << endl; } return os; }
稀疏矩阵的普通逆置:
按列访问,每一次进入之后判断压缩存储的一维数组中是否有对应列的元素,有的话,输出到新空间,并且向后移动。
SparseMatrix<T> Transprot() { SparseMatrix<T> temp; temp._row = _col; temp._col = _row; for (size_t i = 0; i < _col;i++) { vector<Trituple<T>>::iterator it = _pData.begin(); while (it != _pData.end()) { if (it->_col == i)//判断列 { temp._pData.push_back(Trituple<T>(i, it->_row, it->_data));//输入时交换行、列 } it++; } } return temp; }
稀疏矩阵的快速逆置:
快速逆置的实现是直接将压缩存储中的一维数组的元素的排列方式变成逆置后的样子,这样说起来可能有点生硬,看图:
首先,按列访问,统计该列是否有有限元素存在,如果有,给对应的列的数组下边的值加一
int* _pCount=new int[_col];//最多有_col列_pCount[it->_col]++;//此时it表示有限元素的迭代器,it->_col表示它的列。
具体代码:
int* _pCount = new int[_col]; memset(_pCount, 0, _col*sizeof(_pCount[0])); for (size_t i = 0; i < _col; i++)/*按列访问,每列中有多少个有效元素, 本数组中,有5列,从下标为0的第一列开始计*/ { vector<Trituple<T>>::iterator it = _pData.begin();/*每次进来it从_pData的开始走,判断pData 中的元素有没有列和判断的列相同的,如果有,则证明: _pData在检测的当前列中存在有效元素,给有效元素的下标位置的_pCount 加1,因为数组有多行,每一列的元素不止一个,每一列可能有多个有效元素 ,所以要使用it遍历_pData。*/ while (it != _pData.end()) { if (it->_col == i)/*it代表_pData的当前元素的指针,当其列==检测列时*/ _pCount[it->_col]++;/*给_pCount中it代表的数的下标位置加一,刚好_pCount的下标与有效元素的列相对应*/ it++; } }
我们还应该保存每个有效元素的起始位置,因为原矩阵的列转置后就变成了新矩阵的行,所以我们只需保存原矩阵中每个有效元素的起始列就行。且第一列的起始地址始终未0,下一列的起始地址等于上一列的起始地址+上一列的有效元素的个数。
int* _pAddar = new int[_col]; memset(_pAddar, 0, _col*sizeof(_pAddar[0]));/*置0时,因为第一行的起始地址为0,第一列的起始地址就已经置为0*/ for (size_t i = 1; i < _col; i++)//所以从1开始 { _pAddar[i] = _pAddar[i - 1] + _pCount[i - 1]; }
放置有效元素到新空间–>“逆置”_pData
如果不懂的话仔细看代码中的注释。
for (size_t i = 0; i < _pData.size(); i++) { temp._pData[_pAddar[_pData[i]._col]] = Trituple<T>(_pData[i]._col, _pData[i]._row, _pData[i]._data); /*_pAddar[_pData[i]._col]: _pAddar保存的是新数组中有效元素的起始位置,也是原压缩数组有效元素的列的起始位置 所以原数组第i个元素的列的位置就是新数组有效元素的行的起始位置 也就是第i个元素在新数组中的行的起始位置 所以将原数组的有效元素放置到新数组其对应的行的起始位置 且存储的时候将该元素的行列交换*/ _pAddar[_pData[i]._col]++; /*放置完后,再给新数组的行的起始位置加一,因为当前行已经有一个元素存入, 它的起始位置应该向后移动一位 按行优先级访问,该行下一个元素在该元素的下个位置*/ }
两个同行同列的矩阵的相加其实就如:两个有序单链表合并后依然有序的算法一样。
将同行同列的元素相加,并push入对象中,但注意:如果加起来为无效值的话则略过不计;如果行、列值不等,则push小的那个。结束循环的条件为:任何一个已经遍历完毕(虽然同行同列,但是有效元素的数目不一定相同)。然后将未遍历完的矩阵的有限元素直接push入就可。
SparseMatrix<T> operator+(const SparseMatrix<T>& sp) { SparseMatrix<T> temp; temp._row = _row; temp._col = _col; size_t i = 0, j = 0; size_t Size1 = _pData.size(); size_t Size2 = sp._pData.size(); while (i < Size1 && j < Size2) { if ((_pData[i]._row == sp._pData[j]._row) && (_pData[i]._col == sp._pData[j]._col)) { if (_pData[i]._data + sp._pData[i]._data!=_invalid) temp._pData.push_back((Trituple<T>(_pData[i]._row, _pData[i]._col, _pData[i]._data + sp._pData[j]._data))); i++; j++; } else if ((_pData[i]._row > sp._pData[j]._row) || (_pData[i]._col > sp._pData[j]._col)) { temp._pData.push_back(sp._pData[j]); j++; } else if ((_pData[i]._row < sp._pData[j]._row) || (_pData[i]._col < sp._pData[j]._col)) { temp._pData.push_back(_pData[i]); i++; } } if (i >= Size1) { for (; j < Size2; j++) temp._pData.push_back(sp._pData[j]); } if (j >= Size2) { for (; i < Size1; i++) temp._pData.push_back(_pData[i]); } return temp; }
关于稀疏矩阵的所有代码以及测试用例如下:
#include <vector>#include <iomanip>#include <iostream>using namespace std;template<class T>class SparseMatrix{ template<class T> struct Trituple { Trituple(size_t row, size_t col, const T& data) : _row(row) , _col(col) , _data(data) {} Trituple() {} size_t _row; size_t _col; T _data; };public: // 稀疏矩阵的压缩存储 SparseMatrix(int* array, size_t row, size_t col, const T& invalid) : _row(row) , _col(col) , _invalid(invalid) { for (size_t i = 0; i < _row; i++) { for (size_t j = 0; j < _col; j++) { if (array[i*_col + j] != _invalid) _pData.push_back(Trituple<T>(i, j, array[i*_col + j])); } } } SparseMatrix() {} // 访问稀疏矩阵中row行col中的元素 T& Access(int row, int col) { for (size_t i = 0; i < _pData.size();++i) { if (_pData[i]._row == row&&_pData[i]._col == col) return _pData[i]._data; } return _invalid; } // 稀疏矩阵的逆置 SparseMatrix<T> Transprot() { SparseMatrix<T> temp; temp._row = _col; temp._col = _row; for (size_t i = 0; i < _col;i++) { vector<Trituple<T>>::iterator it = _pData.begin(); while (it != _pData.end()) { if (it->_col == i) { temp._pData.push_back(Trituple<T>(i, it->_row, it->_data)); } it++; } } return temp; } // 稀疏矩阵的快速逆置 SparseMatrix<T> FastTransprot() { SparseMatrix<T> temp; temp._row = _col; temp._col = _row; for (size_t i = 0; i < _pData.size(); i++) temp._pData.push_back(Trituple<T>()); //统计每列中有效元素的个数 int* _pCount = new int[_col]; memset(_pCount, 0, _col*sizeof(_pCount[0])); for (size_t i = 0; i < _col; i++)/*按列访问,每列中有多少个有效元素*/ { vector<Trituple<T>>::iterator it = _pData.begin();/*每次进来it从_pData的开始走,判断pData 中的元素有没有列和判断的列相同的,如果有,则证明: _pData在检测的当前列中存在有效元素,给有效元素的下标位置的_pCount 加1,因为数组有多行,每一列的元素不止一个,每一列可能有多个有效元素 ,所以要使用it遍历_pData。*/ while (it != _pData.end()) { if (it->_col == i)/*it代表_pData的当前元素的指针,当其列==检测列时*/ _pCount[it->_col]++;/*给_pCount中it代表的数的下标位置加一,刚好_pCount的下标与有效元素的列相对应*/ it++; } } //用数组保存每列元素在新矩阵中的起始地址 int* _pAddar = new int[_col]; memset(_pAddar, 0, _col*sizeof(_pAddar[0]));/*置0时,因为第一行的起始地址为0,第一列的起始地址就已经置为0*/ for (size_t i = 1; i < _col; i++)//所以从1开始 { _pAddar[i] = _pAddar[i - 1] + _pCount[i - 1]; } //放置有效元素到新空间-->“逆置”_pData for (size_t i = 0; i < _pData.size(); i++) { temp._pData[_pAddar[_pData[i]._col]] = Trituple<T>(_pData[i]._col, _pData[i]._row, _pData[i]._data); /*_pAddar[_pData[i]._col]: _pAddar保存的是新数组中有效元素的起始位置,也是原压缩数组有效元素的列的起始位置 所以原数组第i个元素的列的位置就是新数组有效元素的行的起始位置 也就是第i个元素在新数组中的行的起始位置 所以将原数组的有效元素放置到新数组其对应的行的起始位置 且存储的时候将该元素的行列交换*/ _pAddar[_pData[i]._col]++; /*放置完后,再给新数组的行的起始位置加一,因为当前行已经有一个元素存入, 它的起始位置应该向后移动一位 按行优先级访问,该行下一个元素在该元素的下个位置*/ } return temp; } // 实现稀疏矩阵的加法操作 SparseMatrix<T> operator+(const SparseMatrix<T>& sp) { SparseMatrix<T> temp; temp._row = _row; temp._col = _col; size_t i = 0, j = 0; size_t Size1 = _pData.size(); size_t Size2 = sp._pData.size(); while (i < Size1 && j < Size2) { if ((_pData[i]._row == sp._pData[j]._row) && (_pData[i]._col == sp._pData[j]._col)) { if (_pData[i]._data + sp._pData[i]._data!=_invalid) temp._pData.push_back((Trituple<T>(_pData[i]._row, _pData[i]._col, _pData[i]._data + sp._pData[j]._data))); i++; j++; } else if ((_pData[i]._row > sp._pData[j]._row) || (_pData[i]._col > sp._pData[j]._col)) { temp._pData.push_back(sp._pData[j]); j++; } else if ((_pData[i]._row < sp._pData[j]._row) || (_pData[i]._col < sp._pData[j]._col)) { temp._pData.push_back(_pData[i]); i++; } } if (i >= Size1) { for (; j < Size2; j++) temp._pData.push_back(sp._pData[j]); } if (j >= Size2) { for (; i < Size1; i++) temp._pData.push_back(_pData[i]); } return temp; } // 还原稀疏矩阵 template<class T> friend ostream& operator<<(ostream& os, SparseMatrix<T>& s) { size_t index = 0; for (size_t i = 0; i < s._row; ++i) { for (size_t j = 0; j < s._col; ++j) { if ((index < s._pData.size()) && (s._pData[index]._row == i) && (s._pData[index]._col == j)) os << setw(3) << s._pData[index++]._data << " "; else os << setw(3) << s._invalid << " "; } os << endl; } return os; }private: vector<Trituple<T>> _pData; size_t _row; size_t _col; T _invalid;};int main(){ int array[][5] = { { 0, 6, 0, 0, 0 }, { 1, 0, 0, 0, 0 }, { 0, 2, 0, 0, 0 }, { 0, 0, 0, 0, 0 }, { 0, 4, 0, 0, 0 }, { 0, 0, 0, 7, 0 } }; int array1[][5] = { { 0, 0, 0, 0, 0 }, { 0, 2, 0, 0, 0 }, { 0, 0, 0, 4, 0 }, { 0, 0, 7, 0, 5 }, { 0, 6, 0, 0, 0 }, { 0, 0, 5, 0, 0 } }; SparseMatrix<int> sp((int*)array, 6, 5, 0); cout << sp << endl; cout << sp.Transprot() << endl; cout << sp.FastTransprot() << endl; SparseMatrix<int> sp1((int*)array1, 6, 5, 0); SparseMatrix<int> sp2 = sp + sp1; cout << sp2 << endl; system("pause"); return 0;}
如有问题,敬请指出。
- 稀疏矩阵的访问、普通逆置和快速逆置、还原输出以及加法
- 稀疏矩阵的压缩存储和逆置,以及快速逆置,以及稀疏矩阵的加法
- 动态稀疏矩阵对应的三元组的输出,以及逆置和赋值算法
- 稀疏矩阵的压缩存储和快速逆置
- 稀疏矩阵的快速逆置和相加
- 稀疏矩阵的压缩储存,稀疏矩阵的普通转置,稀疏矩阵的快速转置
- 对称矩阵、稀疏矩阵及矩阵的逆置与加法
- 稀疏矩阵的压缩存储以及快速转置
- 稀疏矩阵的(普通/快速)转置
- java和js实现普通矩阵和稀疏矩阵(非满矩阵)的转置
- 数据结构练习---java实现普通矩阵与稀疏矩阵的矩阵转置,矩阵加法,矩阵乘法,应用简单工厂与模板方法。
- 稀疏矩阵(实现快速转置和普通转置)
- 稀疏矩阵的存储以及转置、加法、乘法操作实现
- 【C++】稀疏矩阵的普通转置与快速转置
- 稀疏矩阵的普通转置与快速转置算法
- 稀疏矩阵的转置和快速转置
- 稀疏矩阵的存储和快速转置
- 实验五:稀疏矩阵的存储和快速转置
- mysql远程连接
- robotframework-ride 运行报monitorcolors not recognized
- jquery技巧之div绑定resize10:30
- 线程的join和detach
- pyCharm使用
- 稀疏矩阵的访问、普通逆置和快速逆置、还原输出以及加法
- 数量金融学(4):投资风险的测量和业绩评价
- linux下的shell输入输出重定向
- Android内存管理
- 关于Bootstrap (Web框架)
- Python之自省(反射)
- 修改index页面的搜索
- python字典学习
- 85