稀疏矩陣的特色
算法
M*N矩陣,矩陣中有效值的個數遠遠小於無效值的個數,而且這些數據的分佈沒有規律。數組
例以下面的矩陣ide
稀疏矩陣的壓縮存儲函數
壓縮矩陣值存儲極少數的有效數據。使用三元組來存儲每個數據,三元組數據按照矩陣中的位置,以行優先順序依次存放。性能
則上述矩陣的存儲結構爲spa
三元組結構
blog
//三元組的定義 template<class T> struct Triple { public: //默認無參構造函數 Triple() {} //構造函數 Triple(const T& d,size_t row=0,size_t col=0) :_value(d) ,_row(row) ,_col(col) {} public: T _value;//數據域 size_t _row;//行 size_t _col;//列 };
稀疏矩陣的壓縮ip
template<class T> class SparseMatrix { public: //無參構造函數 SparseMatrix() {} SparseMatrix(const T* a, size_t row, size_t col, const T& invalid); void Display();//打印 SparseMatrix<T> Transport();//列轉置 SparseMatrix<T> FastTransport();//快速轉置 private: vector<Triple<T>> _a;//三元組類型的順序表 size_t _rowSize;//行數 size_t _colSize;//列數 T _invalid;//非法值 }; //矩陣的壓縮 template<class T> SparseMatrix<T>::SparseMatrix(const T* a, size_t row, size_t col, const T& invalid) :_rowSize(row) , _colSize(col) , _invalid(invalid) { //行遍歷 for (size_t i = 0; i < row; i++) { //列遍歷 for (size_t j = 0; j < col; j++) { if (a[i*col + j] != invalid) { _a.push_back(Triple<T>(a[i*col + j], i, j)); //不能經過數組建立,可是能夠經過數組形式訪問 } } } }
轉置
get
將原矩陣的行、列互換,也就是將[row][col]和[col][row]位置上的數據對換。it
列轉置
算法思想:
採用按照被轉置矩陣三元組表A的序列(即轉置後三元組表B的行序)遞增的順序進行轉置,並以此送入轉置後矩陣的算遠足表B中,這樣一來,轉置後矩陣的三元組表B剛好是以「行序爲主序的」.
實現代碼
//列轉置 template<class T> SparseMatrix<T> SparseMatrix<T>::Transport() { SparseMatrix<T> result; //行列size互換 result._rowSize = _colSize; result._colSize = _rowSize; result._invalid = _invalid; //按照列掃描 for (size_t j = 0; j < _colSize; j++) { //在三元組數組中找到相同列的元素 for (size_t index = 0; index < _a.size(); index++) { if (_a[index]._col == j) { result._a.push_back(Triple<T>(_a[index]._value, _a[index]._col, _a[index]._row)); //按照列優先的順序存到壓縮數組中 } } } return result; }
算法分析:
算法的時間主要消耗在雙重循環中,其時間複雜度爲O(_colSize*_a.size)。
一次定位快速轉置
算法思想:
在列轉置中算法的時間浪費主要在雙重循環中,要改善算法的性能,必須去掉雙重循環,使得整個轉置過程經過一次循環來完成。
爲了能使得被轉置的三元組表A中元素一次定位到三元組表B中,須要預先計算一下數據:
1)rowCounts,三元組表A中每一列有效值的個數,即轉置後矩陣三元組表B中每一行有效值的個數。
2)rowStart,三元組表B中每一行有效值的起始位置。
rowStart[col]=rowStart[col-1]+rowCounts[col-1]
上述矩陣的兩個數組爲:
代碼實現:
//快速轉置 template<class T> SparseMatrix<T> SparseMatrix<T>::FastTransport() { assert(_a.size() < 0); SparseMatrix<T> result; //行列size互換 result._rowSize = _colSize; result._colSize = _rowSize; result._invalid = _invalid; //創建rowCounts和rowStart int* rowCounts = new int[_colSize]; int* rowStart = new int[_colSize]; memset(rowCounts, 0, sizeof(int)*_colSize);//初始化爲0 memset(rowStart, 0, sizeof(int)*_colSize);//初始化爲0 result._a.resize(_a.size());//複製順序表_a,容量相同,可是數據不一樣。 //初始化 for (size_t i = 0; i < _colSize; i++) { rowCounts[_a[i]._col]++; } rowStart[0] = 0; for (size_t i = 1; i < _colSize; i++) { rowStart[i] = rowCounts[i - 1] + rowStart[i - 1]; } //快速轉置 size_t index = 0; Triple<T> tmp; while (index < _a.size()) { int row = _a[index]._col;//行數 int rowIndex = rowStart[row];//當前行的起始位置 //交換行和列 tmp._value = _a[index]._value; tmp._row = _a[index]._col; tmp._col = _a[index]._row; result._a[rowIndex] = tmp; rowStart[row]++; index++; } delete[] rowCounts; delete[] rowStart; return result; }
算法分析:
一次定位快速轉置算法時間主要浪費在3個並列的循環中,分別執行了_colSize,_col_Size,_a.size()次,總的時間複雜度爲O(_colSize+_a.size())。遠遠小於O(_colSize*_a.size)。