1.什么是稀疏矩阵?
稀疏矩阵就是矩阵中有大量零元素
2.稀疏矩阵的好处
节省空间
3.稀疏矩阵的类模板定义:
#ifndef TRIPLE_H_INCLUDED #define TRIPLE_H_INCLUDED template <class ElemType> struct Triple { int row,col; ElemType value; Triple() { } Triple(int r, int c ,ElemType v) { row=r; col=c; value=v; } }; #endif // TRIPLE_H_INCLUDED
(三元组定义)
1 #ifndef TRISPARSEMATRIX_H_INCLUDED 2 #define TRISPARSEMATRIX_H_INCLUDED 3 #include "Triple.h" 4 #include "Assistance.h"//一些通用的辅助功能,和稀疏矩阵无关 5 template <class ElemType> 6 class TriSparseMatrix 7 { 8 protected: 9 Triple<ElemType> *triElems; 10 int maxSize; 11 int rows,cols,num; 12 public: 13 TriSparseMatrix(int rs=100,int cs=100,int size=10000); 14 ~TriSparseMatrix(); 15 int getRows() const; 16 int getCols() const; 17 int getNum() const; 18 Status setElem(int r,int c ,const ElemType &v); 19 Status getElem(int r,int c,ElemType &v); 20 TriSparseMatrix(const TriSparseMatrix<ElemType> ©); 21 void simpleTranspose(TriSparseMatrix<ElemType> &b); 22 void fastTranspose(TriSparseMatrix<ElemType> &b); 23 TriSparseMatrix<ElemType> &operator =(const TriSparseMatrix<ElemType> ©); 24 }; 25 26 27 #endif // TRISPAREMATRIX_H_INCLUDED
(三元组顺序表稀疏矩阵类)
4.三元组顺序表的相关函数实现
<1>修改稀疏矩阵的函数
template <class ElemType> Status TriSparseMatrix<ElemType>::setElem(int r,int c,const ElemType &v) { if (r >= rows || c >= cols || r < 0 || c < 0) return RANGE_ERROR; // 下标范围错 int i, j; // 工作变量 for (j = num - 1; j >= 0 && (r < =triElems[j].row && c < triElems[j].col); j--);// 查找三元组位置 if (j >= 0 && triElems[j].row == r && triElems[j].col == c) { // 找到三元组 if (v == 0) { // 删除三元组 for (i = j + 1; i < num; i++) triElems[i - 1] = triElems[i]; // 前移从j+1开始的三元组 num--; // 删除三元组后,非零元个数自减1 } else // 修改元素值 triElems[j].value = v; return SUCCESS; // 成功 } else if (v != 0) { if (num < maxSize) { // 将三元组(r, c, v)插入到三元组表中 for (i = num - 1; i > j; i--) // 后移元素 triElems[i + 1] = triElems[i]; // j + 1为空出的插入位置 triElems[j + 1].row = r; // 行 triElems[j + 1].col = c; // 列 triElems[j + 1].value = v; // 非零元素值 num++; // 插入三元组后,非零元个数自加1 return SUCCESS; // 成功 } else // 溢出 return OVER_FLOW; // 溢出时返回OVER_FLOW } return SUCCESS; }
解释:这段代码主要的作用是改变某个三元组的值。(1)如果原来(r,c)位置存在非零元素,如果要修改v==0.那么就应该删除该三元组,因为稀疏矩阵式不存储零元素的。如果要修改的v!=0,那么直接赋值就好(2)如果原来(r,c)位置不存在并且v!=0那么就就要插入这个(r,c,v)的三元组,如果v==0那么也不用管了。理由同上。
<2>稀疏矩阵的转置函数
首先介绍简单转置方法。大致思想就是从第一列到最后一列,先找出每一列的所有三元组然后row和col互换。代码实现如下:
template <class ElemType> void TriSparseMatrix<ElemType>::simpleTranspose(TriSparseMatrix<ElemType> &b) { b.rows=cols; b.cols=rows; b.num=num; b.maxSize=maxSize; if(b.triElems!=NULL) delete [] b.triElems; b.triElems=new Triple<ElemType>[b.maxSize]; if(b.num>0) { int i=0; for(int col=0;col<cols;col++) { for(int j=0;j<num;j++) { if(triElems[j].col==col) { b.triElems[i].row=triElems[j].col; b.triElems[i].col=triElems[j].row; b.triElems[i].value=triElems[j].value; i++; } } } } }
(简单转置)
上述的算法时间复杂度是O(rows*cols)
下面介绍快速转置:
首先算出来每一列的非零元素(cNum),然后根据cNum算出来转置后的矩阵的存储位置。大致思想就是这样,下面的是代码实现
template <class ElemType> void TriSparseMatrix<ElemType>::fastTranspose(TriSparseMatrix<ElemType> &b) { b.rows=cols; b.cols=rows; b.num=num; b.maxSize=maxSize; delete [] b.triElems; b.triElems=new Triple<ElemType>[b.maxSize]; int *cNum=new int [cols+1];// 存放原矩阵中每一列的非零元个数 int *cPos=new int [cols+1];// 存放原矩阵中每一列的第一个非零元在b中的存储位置 int col,i; if(b.num>0) { for (col=0;col<cols;col++) { cNum[col]=0;//初始化cNum } for(i=0;i<num;i++)// 统计原矩阵中每一列的非零元个数 ++cNum[triElems[i].col]; cPos[0]=0;// 第一列的第一个非零元在b存储的起始位置 for(col=1;col<cols;col++)// 循环求每一列的第一个非零元在b存储的起始位置 cPos[col]=cPos[col-1]+cNum[col-1]; for(i=0;i<num;i++) { int j=cPos[triElems[i].col];//用于表示b当前列的下一个非零元素位置 b.triElems[j].row=triElems[i].col; b.triElems[j].col=triElems[i].row; b.triElems[j].value=triElems[i].value; ++cPos[triElems[i].col]; } } delete [] cNum; delete [] cPos; }
(快速转置)
以下面的矩阵为例说明一下快速转置的流程
15, 0, 0, 22, 0, -5,
0, 11, 3, 0, 0, 0,
0, 0, 0, 6, 0, 0,
0, 0, 0, 0, 0, 0,
91, 0, 0, 0, 0, 0,
0, 7, 0, 0, 0, 0,
0, 0, 28, 0, 0, 0
可以求出CNum={2,2,2,2,0,1},原始矩阵的第一列的第一个非零元素在转置矩阵的位置肯定是row=0,col=0;也就是triElemd[0],第一列的下面的元素一次递增。如果到第二列的话,那么第二列的第一个非零元素的位置应该是第一列的第一个非零元素在转置矩阵的位置(第一列的第一元素在转置矩阵的位置是triElem[0])加上第一列的非零元素的总数teiElem[2]。一次类推,原矩阵第三列的第一个非零元素在倒置矩阵的位置是原矩阵中第二列第一个非零元素在倒置矩阵的位置加上第二列的非零元素的个数也就是teiElem[4]。
下面贴下完整的代码
1 #include "TriSparseMatrix.h" 2 #include "Triple.h" 3 #include "Assistance.h" 4 #include <iostream> 5 using namespace std; 6 template <class ElemType> 7 TriSparseMatrix<ElemType>::TriSparseMatrix(int rs,int cs,int size) 8 { 9 if(rs<1||cs<1) 10 throw Error("行数或者列数无效!"); 11 maxSize=size; 12 num=0; 13 rows=rs; 14 cols=cs; 15 triElems=new Triple<ElemType>[maxSize]; 16 } 17 template <class ElemType> 18 TriSparseMatrix<ElemType>::TriSparseMatrix(const TriSparseMatrix<ElemType> ©) 19 // 操作结果:由稀疏矩阵copy构造新稀疏矩阵——复制构造函数 20 { 21 maxSize = copy.maxSize; // 最大非零元素个数 22 triElems = new Triple<ElemType>[maxSize]; // 分配存储空间 23 rows = copy.rows; // 复制行数 24 cols = copy.cols; // 复制列数 25 num = copy.num; // 复制非零元素个数 26 triElems = new Triple<ElemType>[maxSize]; // 为三元组分配存储空间 27 for (int i = 0; i < num; i++) // 复制三元组 28 triElems[i] = copy.triElems[i]; 29 } 30 31 template <class ElemType> 32 TriSparseMatrix<ElemType>::~TriSparseMatrix() 33 { 34 if(triElems!=NULL) 35 delete [] triElems; 36 } 37 template <class ElemType> 38 int TriSparseMatrix<ElemType>::getRows() const 39 { 40 return rows; 41 } 42 template <class ElemType> 43 int TriSparseMatrix<ElemType>::getCols() const 44 { 45 return cols; 46 } 47 template <class ElemType> 48 int TriSparseMatrix<ElemType>::getNum() const 49 { 50 return num; 51 } 52 template <class ElemType> 53 Status TriSparseMatrix<ElemType>::setElem(int r,int c,const ElemType &v) 54 { 55 if (r >= rows || c >= cols || r < 0 || c < 0) 56 return RANGE_ERROR; // 下标范围错 57 int i, j; // 工作变量 58 for (j = num - 1; j >= 0 && (r < =triElems[j].row && c < triElems[j].col); j--);// 查找三元组位置 59 60 if (j >= 0 && triElems[j].row == r && triElems[j].col == c) 61 { // 找到三元组 62 if (v == 0) { // 删除三元组 63 for (i = j + 1; i < num; i++) 64 triElems[i - 1] = triElems[i]; // 前移从j+1开始的三元组 65 num--; // 删除三元组后,非零元个数自减1 66 } 67 else // 修改元素值 68 triElems[j].value = v; 69 return SUCCESS; // 成功 70 } 71 else if (v != 0) { 72 if (num < maxSize) { // 将三元组(r, c, v)插入到三元组表中 73 for (i = num - 1; i > j; i--) // 后移元素 74 triElems[i + 1] = triElems[i]; 75 // j + 1为空出的插入位置 76 triElems[j + 1].row = r; // 行 77 triElems[j + 1].col = c; // 列 78 triElems[j + 1].value = v; // 非零元素值 79 num++; // 插入三元组后,非零元个数自加1 80 return SUCCESS; // 成功 81 } 82 else // 溢出 83 return OVER_FLOW; // 溢出时返回OVER_FLOW 84 } 85 return SUCCESS; 86 } 87 template <class ElemType> 88 Status TriSparseMatrix<ElemType>::getElem(int r, int c, ElemType &v) 89 { 90 if(r>=rows||c>=cols||r<0||c<0) 91 { 92 return RANGE_ERROR; 93 } 94 int j; 95 for(j=num-1;j>=0&&(r<triElems[j].row||r==triElems[j].row&&c<triElems[j].col);j--);//查找制定三元组的位置 96 if(j>=0&&triElems[j].row==r&&triElems[j].col==c) 97 { 98 v=triElems[j].value; 99 } 100 else 101 { 102 v=0; 103 } 104 return SUCCESS; 105 } 106 107 template <class ElemType> 108 TriSparseMatrix<ElemType> &TriSparseMatrix<ElemType>::operator =(const TriSparseMatrix<ElemType> ©) 109 { 110 if(©!=this) 111 { 112 maxSize=copy.maxSize; 113 if(triElems!=NULL) 114 delete [] triElems; 115 triElems=new Triple<ElemType>[maxSize]; 116 rows=copy.rows; 117 cols=copy.cols; 118 num=copy.num; 119 for(int i=0;i<num;i++) 120 { 121 triElems[i]=copy.triElems[i]; 122 } 123 } 124 return *this; 125 126 } 127 /* 128 *简单转置,大致思路先找出一列的所用元素,row和col互换 129 */ 130 template <class ElemType> 131 void TriSparseMatrix<ElemType>::simpleTranspose(TriSparseMatrix<ElemType> &b) 132 { 133 b.rows=cols; 134 b.cols=rows; 135 b.num=num; 136 b.maxSize=maxSize; 137 if(b.triElems!=NULL) 138 delete [] b.triElems; 139 b.triElems=new Triple<ElemType>[b.maxSize]; 140 if(b.num>0) 141 { 142 int i=0; 143 for(int col=0;col<cols;col++) 144 { 145 for(int j=0;j<num;j++) 146 { 147 if(triElems[j].col==col) 148 { 149 b.triElems[i].row=triElems[j].col; 150 b.triElems[i].col=triElems[j].row; 151 b.triElems[i].value=triElems[j].value; 152 i++; 153 } 154 } 155 } 156 } 157 } 158 159 template <class ElemType> 160 void TriSparseMatrix<ElemType>::fastTranspose(TriSparseMatrix<ElemType> &b) 161 { 162 b.rows=cols; 163 b.cols=rows; 164 b.num=num; 165 b.maxSize=maxSize; 166 delete [] b.triElems; 167 b.triElems=new Triple<ElemType>[b.maxSize]; 168 int *cNum=new int [cols+1];// 存放原矩阵中每一列的非零元个数 169 int *cPos=new int [cols+1];// 存放原矩阵中每一列的第一个非零元在b中的存储位置 170 int col,i; 171 if(b.num>0) 172 { 173 for (col=0;col<cols;col++) 174 { 175 cNum[col]=0;//初始化cNum 176 } 177 for(i=0;i<num;i++)// 统计原矩阵中每一列的非零元个数 178 ++cNum[triElems[i].col]; 179 cPos[0]=0;// 第一列的第一个非零元在b存储的起始位置 180 for(col=1;col<cols;col++)// 循环求每一列的第一个非零元在b存储的起始位置 181 cPos[col]=cPos[col-1]+cNum[col-1]; 182 for(i=0;i<num;i++) 183 { 184 int j=cPos[triElems[i].col];//用于表示b当前列的下一个非零元素位置 185 b.triElems[j].row=triElems[i].col; 186 b.triElems[j].col=triElems[i].row; 187 b.triElems[j].value=triElems[i].value; 188 ++cPos[triElems[i].col]; 189 } 190 } 191 delete [] cNum; 192 delete [] cPos; 193 }
(完整的模板类实现)
上课的时候我也是想了很久,才理解,所以写了下来。写的不好请大家见谅!还有代码呢,很大和老师给的代码几乎都一样,我是一句句敲下来的,看见相同的代码不要见怪!