对称矩阵及对称矩阵的压缩存储
设一个N*N的方阵A,A中任意元素Aij,当且仅当Aij == Aji(0 <= i <= N-1 && 0 <= j <= N-1),则矩阵A是对称矩阵。以矩阵的对角线为分隔,分为上三角和下三角。
压缩存储称矩阵存储时只需要存储上三角/下三角的数据,所以最多存储n(n+1)/2个数据。
对称矩阵和压缩存储的对应关系:下三角存储i>=j,SymmetricMatrix[i][j] == Array[i*(i+1)/2+j]。
实现代码如下:
#include <iostream> using namespace std; template <class T> class SymmetricMatrix { public: SymmetricMatrix(T * a, size_t size) :_a(new T[size*(size+1)/2]) ,_size(size*(size + 1) / 2) ,_n(size) { size_t index = 0; for (size_t i = 0; i < size; i++) { for (size_t j = 0; j < size; j++) { if (i >= j) { _a[index++] = a[size*i + j];//按照一维数组的方式存入数据 } else { break; } } } } ~SymmetricMatrix() { if (_a) { delete[]_a; _a = NULL; _size = 0; } } T& Access(size_t i, size_t j) { if (i < j) { swap(i, j);//当是上三角时,交换i和j } return _a[i*(i + 1) / 2 + j]; } void display() { for (size_t i = 0; i < _n; i++) { for (size_t j = 0; j < _n; j++) { if (i >= j) { cout << _a[i*(i + 1) / 2 + j] << " "; } else { cout << _a[j*(j + 1) / 2 + i] << " "; } } cout << endl; } } protected: size_t _size;//存储数据的个数 T * _a;//存储之后的对称矩阵的指针 size_t _n;//对称矩阵的大小 };
2.稀疏矩阵
M*N的矩阵,矩阵中有效值的个数远小于无效值的个数,且这些数据的分布没有规律。
比如下面这个矩阵:
(1)稀疏矩阵的压缩存储
压缩存储只存储极少数的有效数据。使用{row,col,value}三元组存储每一个有效数据,三元组按原矩阵中的位置,以行优先级先后顺序依次存放。
那么上面的矩阵压缩存储结果就是:
三元组的定义:
template<typename T> struct Triple//三元组 { Triple<T>::Triple()//无参的构造函数 { } Triple(size_t row, size_t col, T value)//构造函数 :_row(row) ,_col(col) ,_value(value) { } size_t _row;//行 size_t _col;//列 T _value;//值 };
压缩存储的实现代码:
template<typename T> class SparseMatrix { public: SparseMatrix() { } //用vector顺序表来存储三元组的信息 SparseMatrix(T * a, size_t m, size_t n, const T & invalid) :_rowsize(m) ,_colsize(n) ,_invalid(invalid) { for (size_t i = 0; i < m; i++) { for (size_t j = 0; j < n; j++)//按照二元数组的方式进行遍历 { if (a[i*n + j] != invalid)//不是无效数据 { Triple<T> cur(i, j, a[i*n + j]); _a.push_back(cur); } } } } protected: vector <Triple<T>> _a; size_t _rowsize; size_t _colsize; T _invalid; };
(2)稀疏矩阵的转置
将原矩阵的行、列对换,也就是将[i][j]和[j][i]位置上的数据对换。
普通转置(列转置)
按照原矩阵的列优先把vector中的三元组放入新的容器中,并且交换行和列的值
SparseMatrix<T> Transport() { assert(_a.size() < 0); //创建新的矩阵,交换行列的值 SparseMatrix<T> ret; ret._rowsize = _colsize; ret._colsize = _rowsize; ret._invalid = _invalid; //两次循环 for (size_t i = 0; i < _colsize; i++)//按原矩阵的列扫描 { size_t index = 0; while (index < _a.size()) { if (_a[index]._col == i) //如果三元组中的列值=i时 //交换行列的值,放入新的矩阵 { Triple<T> tmp(_a[index]._col, _a[index]._row, _a[index]._value); ret._a.push_back(tmp); } index++; } if (_a.size() == ret._a.size()) { break;//两个容器的大小相同时,break } } return ret; }
用此方法可以有效的转置矩阵,我们来看一下此函数的时间复杂度:O(col * _a.size())——矩阵的列*矩阵的元素总和。
如果元素很多就会浪费很多的时间。有没有办法让两层循环变成一层循环呢?付出空间上的代价,换取时间效率。
快速转置
我们只用一层循环来遍历容器_a中所有元素,并把该元素放到指定的位置。这样我们就需要一个数组rowStar来存放第i个元素所在位置。在定义这个数组之前,我们还需要一个数组rowCount来实现统计矩阵第i行元素的数量。这样我们才能更方便的知道第i个元素应该存放的位置。
SparseMatrix<T> FastTransport() { assert(_a.size() > 0); SparseMatrix<T> ret; ret._rowsize = _colsize;//行列值互换 ret._colsize = _rowsize; ret._invalid = _invalid; int * rowCount = new int[_colsize]; int * rowStart = new int[_colsize]; //初始化rowCount和rowStart为0 memset(rowCount, 0, sizeof(int)* _colsize); memset(rowStart, 0, sizeof(int) * _colsize); //初始化 size_t index = 0; while (index < _a.size()) { rowCount[_a[index]._col]++; ++index; } rowStart[0] = 0; for (size_t i = 1; i < _colsize; i++) { rowStart[i] = rowStart[i - 1] + rowCount[i - 1]; } ret._a.resize(_a.size());//复制顺序表_a,容量相同 index = 0; Triple<T> tmp; while (index < _a.size()) { size_t rowIndex = _a[index]._col;//行数 size_t row = rowStart[rowIndex];//当前行的起始位置 //交换行和列 tmp._col = _a[index]._row; tmp._row = _a[index]._col; tmp._value = _a[index]._value; ret._a[row] = tmp;//将tmp放入ret计算好的位置 rowStart[row]++; index++; } delete[] rowCount; delete[] rowStart; return ret; }
此函数的时间复杂度为O(col + _a.size());和普通转置相比,效率提高了很多。
最后,附上完整代码(稀疏矩阵):
#pragma once #include <iostream> #include <vector> #include <assert.h> using namespace std; template<typename T> struct Triple//三元组 { Triple<T>::Triple()//无参的构造函数 { } Triple(size_t row, size_t col, T value) :_row(row) ,_col(col) ,_value(value) { } size_t _row;//行 size_t _col;//列 T _value;//值 }; template<typename T> class SparseMatrix { public: SparseMatrix()//无参的构造函数 { } //用vector顺序表来存储三元组的信息 SparseMatrix(T * a, size_t m, size_t n, const T & invalid) :_rowsize(m) ,_colsize(n) ,_invalid(invalid) { for (size_t i = 0; i < m; i++) { for (size_t j = 0; j < n; j++) { if (a[i*n + j] != invalid) { Triple<T> cur(i, j, a[i*n + j]); _a.push_back(cur); } } } } void Display()//打印矩阵 { size_t index = 0; for (size_t i = 0; i < _rowsize; i++) { for (size_t j = 0; j < _colsize; 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() { assert(_a.size() 》 0); //创建新的矩阵,交换行列的值 SparseMatrix<T> ret; ret._rowsize = _colsize; ret._colsize = _rowsize; ret._invalid = _invalid; //两次循环 for (size_t i = 0; i < _colsize; i++)//按原矩阵的列扫描 { size_t index = 0; while (index < _a.size()) { if (_a[index]._col == i) //如果三元组中的列值=i时 //交换行列的值,放入新的矩阵 { Triple<T> tmp(_a[index]._col, _a[index]._row, _a[index]._value); ret._a.push_back(tmp); } index++; } if (_a.size() == ret._a.size()) { break;//两个容器的大小相同时,break } } return ret; } //快速转置 SparseMatrix<T> FastTransport() { assert(_a.size() > 0); SparseMatrix<T> ret; ret._rowsize = _colsize;//行列值互换 ret._colsize = _rowsize; ret._invalid = _invalid; int * rowCount = new int[_colsize]; int * rowStart = new int[_colsize]; //初始化rowCount和rowStart为0 memset(rowCount, 0, sizeof(int)* _colsize); memset(rowStart, 0, sizeof(int) * _colsize); //初始化 size_t index = 0; while (index < _a.size()) { rowCount[_a[index]._col]++; ++index; } rowStart[0] = 0; for (size_t i = 1; i < _colsize; i++) { rowStart[i] = rowStart[i - 1] + rowCount[i - 1]; } ret._a.resize(_a.size());//复制顺序表_a,容量相同 index = 0; Triple<T> tmp; while (index < _a.size()) { size_t rowIndex = _a[index]._col;//行数 size_t row = rowStart[rowIndex];//当前行的起始位置 //交换行和列 tmp._col = _a[index]._row; tmp._row = _a[index]._col; tmp._value = _a[index]._value; ret._a[row] = tmp;//将tmp放入ret计算好的位置 rowStart[row]++; index++; } delete[] rowCount; delete[] rowStart; return ret; } protected: vector <Triple<T>> _a;//容器 size_t _rowsize;//行 size_t _colsize;//列 T _invalid;//非法值 }; void Test() { int array[5][4] = { { 1, 0, 3, 0, }, { 0, 0, 0, 0, }, { 0, 0, 0, 0, }, { 1, 0, 3, 0, }, { 0, 0, 0, 0, }, }; SparseMatrix<int> sm((int*)array, 5, 4, 0); sm.Display(); SparseMatrix<int> sm1; sm1 = sm.FastTransport(); cout << "转置后的矩阵为: " << endl << endl; sm1.Display(); }
本文出自 “不断进步的空间” 博客,请务必保留此出处http://10824050.blog.51cto.com/10814050/1765046
原文地址:http://10824050.blog.51cto.com/10814050/1765046