稀疏矩阵--三元组表来压缩存储及转置
今天看到第五章《数组和广义表》了,其中的矩阵的存储我其实已经习惯用一个数组了,今天看到这儿,就写了不同的方法来存储(号称能节约大量空间),书(《数据结构c语言版》--严蔚敏、吴伟民 清华大学出版社)上的讲法是:通常,用高级语言编制程序时都是用二维数组来存储矩阵元....然而,在数值分析中经常出现一些阶数很高的矩阵,同时在矩阵中哟许多相同的元素或者是0元素,有时候为了节省存储空间可以对这类矩阵进行压缩存储; 所谓的压缩存储其实就是指的为多个相同值的元只分配一个存储空间,对零元则不分配空间。
首先,稀疏矩阵在书上的定义是:假若值相同的元素或者零元素在矩阵中有一定的分布规律,则称之为特殊矩阵,反之,成为稀疏矩阵;其中特殊矩阵都有自己的方法来压缩,比如,对角矩阵就以对角线的顺序将其压缩到一维数组上,我觉得特殊矩阵各有各的特殊情况,还不如直接用个数组来存储来的方便,在这里就不赘述了。
而稀疏矩阵却是没有确切的定义的,反之元素少到一定的程度就是稀疏了,具体的值是m*n的矩阵中,t个非0元素,若t/(m*n)<=0.05就是了。
上面废话说了那么多,简单的看这张图就知道我的目的了
以上的算法想想是很简单的,因为第一次我是用的时间复杂度为O(m*n)的算法来转置矩阵的,就是两个for循环而已,代码如下:
1 void transMatrix(TSMatrix a,TSMatrix &b){//求三元组顺序表方式,转置矩阵简单的方法,将稀疏矩阵a转置为b
2 b.setRows(a.getCols());//将a 的列数设置为b的行数
3 b.setCols(a.getRows());//将a 的行数设置为b的列数
4 b.setNum(a.getNum());//总数都一样
5 if(b.getNum()>0){//如果总数大于0
6 int flag = 0;
7 for(int i = 0; i < a.getRows();i++){
8 for(int j = 0; j < a.getNum();j++ ){
9 /*
10 没有设置为公共属性,下面的写法看起来有些蛋疼。。,如果都设置为公共属性的话就是这个样子了a.matrix[i].j =
11 下面的操作就是给b矩阵赋值
12 */
13 if(a.getMatrix()[j].getJ() ==i){
14 b.getMatrix()[flag].setI(a.getMatrix()[j].getJ());
15 b.getMatrix()[flag].setJ(a.getMatrix()[j].getI());
16 b.getMatrix()[flag].setData(a.getMatrix()[j].getData());
17 flag++;
18 }
19 }
20 }
21 }
22
23 }
后来,书上的有个快速转置的算法,看了我好久才弄明白,就是所谓的“快速转置法”,如果能预先确定原矩阵的每一列的第一个非零元在转置矩阵中应有的位置,那么在对原矩阵中三元组依次做转换时,就可以直接放到转置矩阵中的合适的位置了,所以在转置前,可以先求的原矩阵中每一列中非零元的个数,进而求得每一列的第一个非零元在转置矩阵中应有的位置,如下表的方式
列数(cols) |
0 | 1 | 2 | 3 | 4 | 5 | 6 |
非0元个数(colsNum[]) | 2 | 2 | 2 | 1 | 0 | 1 | 0 |
第一个非0元位置(colsPos[]) | 0 | 2 | 4 | 6 | 7 | 7 | 8 |
具体的代码如下:
1 void quickTransMatrix(TSMatrix a,TSMatrix &b){
2 b.setRows(a.getCols());//将a 的列数设置为b的行数
3 b.setCols(a.getRows());//将a 的行数设置为b的列数
4 b.setNum(a.getNum());//总数都一样
5
6 int* closNum = new int[a.getCols()];//存储每列的非0元的个数
7 int* closPos = new int[a.getCols()];//存储每列的第一个非零元出现的位置
8 if(a.getNum()>0){
9 for(int i = 0; i < a.getCols();i++){//都初始化为0
10 closNum[i]=0;
11 }
12 for(int i = 0; i < a.getNum();i++){//扫描a里的所有元素,求出每一列中非0元的个数
13 closNum[a.getMatrix()[i].getJ()]++;
14 }
15 closPos[0] = 0;
16 for(int i = 1 ; i < a.getCols();i++){//确定矩阵a中的的第i列中第一个非零元素在b中的位置
17 closPos[i] = closPos[i-1] + closNum[i-1];
18 }
19 for(int i = 0 ; i < a.getNum();i++){
20 int k = closPos[a.getMatrix()[i].getJ()]; //k即矩阵a第j列中第一个非零元素在b中的位置
21 b.getMatrix()[k].setI(a.getMatrix()[i].getJ());
22 b.getMatrix()[k].setJ(a.getMatrix()[i].getI());
23 b.getMatrix()[k].setData(a.getMatrix()[i].getData());
24 closPos[a.getMatrix()[i].getJ()]++;//矩阵a第col列中第一个非零元素在b中的位置向前移动一位
25 }
26 }
27 delete []closNum;
28 delete []closPos;
29 }
总结: 先前的“简单转置法”的时间复杂度为O(m*n) 【m为行数,n为列数,num为矩阵的非零元的个数,后面一样】,而“快速转置法”多用了上表所用了空间,其时间复杂度也有很大的改善,为O(n+num),但是不好的方面就是,三元组存储发来存储只适合按照冲头到尾的顺序来取数据,却不能像数组那样按下表来去了,要找出指定位置的数据,只能遍历这个一维数组了,利弊我也不好权衡,不好定义什么,今后在见识多了再说吧。
下面是写这个的全部代码:
1 #ifndef _TRIPLE_H_
2 #define _TRIPLE_H_
3 #include <iostream>
4 using std::cout;
5 using std::cin;
6 using std::endl;
7 /*
8 Triple为三元组数据的元素,存储有坐标和值
9 */
10 class Triple{
11 private:
12 int i,j;
13 int data;
14 public:
15 Triple();
16 Triple(int i, int j, int data);
17 void setI(int i);
18 void setJ(int j );
19 void setData(int data);
20 int getI();
21 int getJ();
22 int getData();
23 };
24 #endif
1 #include "Triple.h"
2 Triple::Triple(){}
3 Triple::Triple(int i,int j,int data){
4 this->i = i;
5 this->j = j;
6 this->data = data;
7 }
8 void Triple::setI(int i){
9 this->i = i;
10 }
11 void Triple::setJ(int j){
12 this->j = j;
13 }
14 void Triple::setData(int data){
15 this->data = data;
16 }
17 int Triple::getData(){
18 return data;
19 }
20 int Triple::getI(){
21 return i;
22 }
23 int Triple::getJ(){
24 return j;
25 }
1 #ifndef _TSMATRIX_H_
2 #define _TSMATRIX_H_
3 #include "Triple.h"
4 //const int MAXSIZE = 100;//假设非零元个数最大为100,书上的做法
5 /*
6 TSMatrix为三元组存储的矩阵对象
7 */
8 class TSMatrix{
9 private:
10 Triple* matrix;
11 int rows,cols,num;//行数,列数,非零元个数
12 public:
13 TSMatrix();
14 TSMatrix(int rows,int cols,int num);
15 void setRows(int rows);
16 void setCols(int cols);
17 void setNum(int num);
18 void setMatrix(Triple* matrix);
19 int getRows();
20 int getCols();
21 int getNum();
22 Triple* getMatrix();
23 };
24 #endif
1 #include "TSMatrix.h"
2 TSMatrix::TSMatrix(){}
3 TSMatrix::TSMatrix(int rows,int cols,int num){
4 this->rows = rows;
5 this->cols = cols;
6 this->num = num;
7 }
8 void TSMatrix::setRows(int rows){
9 this->rows = rows;
10 }
11 void TSMatrix::setCols(int cols){
12 this->cols = cols;
13 }
14 void TSMatrix::setNum(int num){
15 this->num = num;
16 }
17 void TSMatrix::setMatrix(Triple* matrix){
18 this->matrix = matrix;
19 }
20 int TSMatrix::getRows(){
21 return this->rows;
22 }
23 int TSMatrix::getCols(){
24 return this->cols;
25 }
26 int TSMatrix::getNum(){
27 return this->num;
28 }
29 Triple* TSMatrix::getMatrix(){
30 return this->matrix;
31 }
1 /*
2 2011-7-27 zhangming
3 5.3 矩阵压缩 之稀疏矩阵的压缩
4 方法一:“三元组顺序表”来存储
5 */
6 #include "TSMatrix.h"
7 void transMatrix(TSMatrix a,TSMatrix &b);//求三元组顺序表方式,转置矩阵简单的方法,将稀疏矩阵a转置为b
8 void quickTransMatrix(TSMatrix a,TSMatrix &b);//快速转置法
9 int main(){
10 Triple a_matrix[8];
11 a_matrix[0] = Triple(0,1,12);//先前的错误写法 t[0] = new Triple(1,2,12);
12 a_matrix[1] = Triple(0,2,9);
13 a_matrix[2] = Triple(2,0,-3);
14 a_matrix[3] = Triple(2,5,14);
15 a_matrix[4] = Triple(3,2,24);
16 a_matrix[5] = Triple(4,1,18);
17 a_matrix[6] = Triple(5,0,15);
18 a_matrix[7] = Triple(5,3,-7);
19
20 Triple b_matrix[8];//简单转置法
21 Triple c_matrix[8];//快速转置法
22
23 TSMatrix a = TSMatrix(7,6,8);//建立了一个7*6的数组,里面的非零元有8个
24 TSMatrix b = TSMatrix();
25 TSMatrix c = TSMatrix();
26
27 a.setMatrix(a_matrix);
28 b.setMatrix(b_matrix);
29 c.setMatrix(c_matrix);
30 int flag = 0;
31 cout<<"a矩阵如下:\n";
32 for(int i = 0,flag = 0; i< a.getRows();i++){
33 for(int j = 0; j < a.getCols(); j++){
34 if(a.getMatrix()[flag].getI()==i&&a.getMatrix()[flag].getJ()==j){
35 cout<<a.getMatrix()[flag].getData()<<"\t";
36 flag++;
37 }else{
38 cout<<"0\t";
39 }
40 }
41 cout<<endl;
42 }
43 cout<<endl;
44 cout<<"存储的实际上是这些:";
45 for(int i =0; i < 8;i++){
46 cout<<" ("<<a.getMatrix()[i].getI()<<","<<a.getMatrix()[i].getJ()<<","<<a.getMatrix()[i].getData()<<") ";
47 }
48 cout<<endl;
49 //开始转置
50 transMatrix(a,b);
51 quickTransMatrix(a,c);
52 cout<<endl<<"简单转置法转置后:\n";
53 for(int i = 0,flag = 0; i< b.getRows();i++){
54 for(int j = 0; j < b.getCols(); j++){
55 if(b.getMatrix()[flag].getI()==i&&b.getMatrix()[flag].getJ()==j){
56 cout<<b.getMatrix()[flag].getData()<<"\t";
57 flag++;
58 }else{
59 cout<<"0\t";
60 }
61 }
62 cout<<endl;
63 }
64 cout<<endl;
65 cout<<endl<<"快速转置法转置后:\n";
66 for(int i = 0,flag = 0; i< b.getRows();i++){
67 for(int j = 0; j < b.getCols(); j++){
68 if(b.getMatrix()[flag].getI()==i&&b.getMatrix()[flag].getJ()==j){
69 cout<<b.getMatrix()[flag].getData()<<"\t";
70 flag++;
71 }else{
72 cout<<"0\t";
73 }
74 }
75 cout<<endl;
76 }
77 system("pause");
78 }
79 void transMatrix(TSMatrix a,TSMatrix &b){//求三元组顺序表方式,转置矩阵简单的方法,将稀疏矩阵a转置为b
80 b.setRows(a.getCols());//将a 的列数设置为b的行数
81 b.setCols(a.getRows());//将a 的行数设置为b的列数
82 b.setNum(a.getNum());//总数都一样
83 if(b.getNum()>0){//如果总数大于0
84 int flag = 0;
85 for(int i = 0; i < a.getRows();i++){
86 for(int j = 0; j < a.getNum();j++ ){
87 /*
88 没有设置为公共属性,下面的写法看起来有些蛋疼。。,如果都设置为公共属性的话就是这个样子了a.matrix[i].j =
89 下面的操作就是给b矩阵赋值
90 */
91 if(a.getMatrix()[j].getJ() ==i){
92 b.getMatrix()[flag].setI(a.getMatrix()[j].getJ());
93 b.getMatrix()[flag].setJ(a.getMatrix()[j].getI());
94 b.getMatrix()[flag].setData(a.getMatrix()[j].getData());
95 flag++;
96 }
97 }
98 }
99 }
100
101 }
102 void quickTransMatrix(TSMatrix a,TSMatrix &b){
103 b.setRows(a.getCols());//将a 的列数设置为b的行数
104 b.setCols(a.getRows());//将a 的行数设置为b的列数
105 b.setNum(a.getNum());//总数都一样
106
107 int* closNum = new int[a.getCols()];//存储每列的非0元的个数
108 int* closPos = new int[a.getCols()];//存储每列的第一个非零元出现的位置
109 if(a.getNum()>0){
110 for(int i = 0; i < a.getCols();i++){//都初始化为0
111 closNum[i]=0;
112 }
113 for(int i = 0; i < a.getNum();i++){//扫描a里的所有元素,求出每一列中非0元的个数
114 closNum[a.getMatrix()[i].getJ()]++;
115 }
116 closPos[0] = 0;
117 for(int i = 1 ; i < a.getCols();i++){//确定矩阵a中的的第i列中第一个非零元素在b中的位置
118 closPos[i] = closPos[i-1] + closNum[i-1];
119 }
120 for(int i = 0 ; i < a.getNum();i++){
121 int k = closPos[a.getMatrix()[i].getJ()]; //k即矩阵a第j列中第一个非零元素在b中的位置
122 b.getMatrix()[k].setI(a.getMatrix()[i].getJ());
123 b.getMatrix()[k].setJ(a.getMatrix()[i].getI());
124 b.getMatrix()[k].setData(a.getMatrix()[i].getData());
125 closPos[a.getMatrix()[i].getJ()]++;//矩阵a第col列中第一个非零元素在b中的位置向前移动一位
126 }
127 }
128 delete []closNum;
129 delete []closPos;
130 }