稀疏矩阵转置
Description
稀疏矩阵的存储不宜用二维数组存储每个元素,那样的话会浪费很多的存储空间。所以可以使用一个一维数组存储其中的非零元素。这个一维数组的元素类型是一个三元组,由非零元素在该稀疏矩阵中的位置(行号和列号对)以及该元组的值构成。
矩阵转置就是将矩阵行和列上的元素对换。
现在就请你对一个稀疏矩阵进行转置。以下是稀疏矩阵转置的算法描述:
图:稀疏矩阵转置的算法描述
Input
输入的第一行是两个整数r和c(r*c <= 12500),分别表示一个包含很多0的稀疏矩阵的行数和列数。接下来有r行,每行有c个整数,表示这个稀疏矩阵的各个元素。
Output
输出c行,每行有r个整数,每个整数后跟一个空格。该结果为输入稀疏矩阵的转置矩阵。
Sample Input
6 7 0 12 9 0 0 0 0 0 0 0 0 0 0 0 -3 0 0 0 0 14 0 0 0 24 0 0 0 0 0 18 0 0 0 0 0 15 0 0 -7 0 0 0
Sample Output
0 0 -3 0 0 15 12 0 0 0 18 0 9 0 0 24 0 0 0 0 0 0 0 -7 0 0 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0
HINT
提示:
严老师纸质书中用union类型来表示稀疏矩阵类型,这是有问题的,应该使用struct来表示该类型。
注意理解为什么转置算法中,以列从小到大来进行转置。实际上只需一个循环就能够完成转置而不需将列从小到大来处理,转置后的矩阵虽然是正确的但却乱掉了,以至于在各种处理中会增加复杂。(其实就本题而言,如果不以列从小到大处理将导致输出困难,输出的复杂度增加)
总结:
矩阵是一个应用很广泛的工具和课题。看看《黑客帝国》就知道了。现在初步给大家介绍矩阵的操作,以后有机会还会详细讨论矩阵的。
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 #include <math.h> 13 const int INF=0x3f3f3f3f; 14 typedef long long LL; 15 const int mod=1e9+7; 16 const int maxn=1e4+10; 17 using namespace std; 18 19 typedef struct 20 { 21 int row; 22 int col; 23 int val; 24 }Triple; 25 26 typedef struct 27 { 28 Triple date[maxn]; 29 int rows; 30 int cols; 31 int nums; 32 }TSMatrix; 33 34 void TransposeTSMatrix(TSMatrix *A,TSMatrix *B) 35 { 36 B->rows=A->cols; 37 B->cols=A->rows; 38 B->nums=A->nums; 39 if(B->nums > 0) 40 { 41 int cnt=0; 42 for(int i=1;i<=A->cols;i++) 43 { 44 for(int j=1;j<=A->nums;j++) 45 { 46 if(A->date[j].col==i) 47 { 48 cnt++; 49 B->date[cnt].row=A->date[j].col; 50 B->date[cnt].col=A->date[j].row; 51 B->date[cnt].val=A->date[j].val; 52 } 53 } 54 if(cnt == B->nums) 55 break; 56 } 57 } 58 return ; 59 } 60 61 int main() 62 { 63 TSMatrix A,B; 64 scanf("%d %d",&A.rows,&A.cols); 65 A.nums=0; 66 for(int i=1;i<=A.rows;i++) 67 { 68 for(int j=1;j<=A.cols;j++) 69 { 70 int x; 71 scanf("%d",&x); 72 if(x) 73 { 74 A.nums++; 75 A.date[A.nums].row=i; 76 A.date[A.nums].col=j; 77 A.date[A.nums].val=x; 78 } 79 } 80 } 81 TransposeTSMatrix(&A,&B); 82 int cnt=1; 83 for(int i=1;i<=B.rows;i++) 84 { 85 for(int j=1;j<=B.cols;j++) 86 { 87 int a,b; 88 a=B.date[cnt].row; 89 b=B.date[cnt].col; 90 if(a==i&&b==j) 91 { 92 printf("%d ",B.date[cnt].val); 93 cnt++; 94 } 95 else 96 printf("%d ",0); 97 } 98 printf("\n"); 99 } 100 return 0; 101 }
稀疏矩阵快速转置
Description
稀疏矩阵的存储不宜用二维数组存储每个元素,那样的话会浪费很多的存储空间。所以可以使用一个一维数组存储其中的非零元素。这个一维数组的元素类型是一个三元组,由非零元素在该稀疏矩阵中的位置(行号和列号对)以及该元组的值构成。
而矩阵转置就是将矩阵行和列上的元素对换。参考算法5.1中的具体做法,令mu和nu分别代表稀疏矩阵的行数和列数,不难发现其时间复杂度为O(mu×nu)。而当非零元的个数tu与mu×nu同数量级时,算法5.1的时间复杂度将上升至O(mu×nu2)。因此,需要采用快速的稀疏矩阵转置算法。
现在就请你实现一个快速的对稀疏矩阵进行转置的算法。以下是稀疏矩阵快速转置的算法描述:
Input
输入的第一行是两个整数r和c(r<200, c<200, r*c <= 12500),分别表示一个包含很多0的稀疏矩阵的行数和列数。接下来有r行,每行有c个整数,用空格隔开,表示这个稀疏矩阵的各个元素。
Output
输出为读入的稀疏矩阵的转置矩阵。输出共有c行,每行有r个整数,每个整数后输出一个空格。请注意行尾输出换行。
Sample Input
6 7 0 12 9 0 0 0 0 0 0 0 0 0 0 0 -3 0 0 0 0 14 0 0 0 24 0 0 0 0 0 18 0 0 0 0 0 15 0 0 -7 0 0 0
Sample Output
0 0 -3 0 0 15 12 0 0 0 18 0 9 0 0 24 0 0 0 0 0 0 0 -7 0 0 0 0 0 0 0 0 14 0 0 0 0 0 0 0 0 0
HINT
提示:这个算法仅比算法5.1多用了两个辅助向量。对于这个算法的时间复杂度,不难发现算法中有4个并列的单循环,循环次数分别为nu和tu,因而总的时间复杂度为O(nu+tu)。而当稀疏矩阵的非零元个数tu和mu×nu的数量级相同时,其时间复杂度为O(mu×nu),与经典算法的时间复杂度相同。请注意理解为什么转置算法中,以列从小到大来进行转置。实际上只需一个循环就能够完成转置而不需将列从小到大来处理,转置后的矩阵虽然内容正确,但元素的顺序却发生了变化,以至于在后续的各种处理操作中会增加复杂度。而在本题中,如果不按照列从小到大的顺序处理将导致输出困难,大大增加输出的复杂度。总结:稀疏矩阵是矩阵应用中很重要的一部分,由于其元素稀疏的特殊性质,我们可以得到比传统矩阵算法更快速的特殊算法。这也将会在本章后面的题目中得到体现。
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 const int INF=0x3f3f3f3f; 13 typedef long long LL; 14 const int mod=1e9+7; 15 const int maxn=1e4+10; 16 using namespace std; 17 18 typedef struct 19 { 20 int row;//行 21 int col;//列 22 int val;//值 23 }Triple; 24 25 typedef struct 26 { 27 Triple data[maxn];//三元组 28 int rows;//矩阵的总行数 29 int cols;//矩阵的总列数 30 int nums;//矩阵的非零元素个数 31 }TSMatrix; 32 33 //void TransposeTSMatrix(TSMatrix *A,TSMatrix *B)//矩阵的转置 34 //{ 35 // B->rows=A->cols; 36 // B->cols=A->rows; 37 // B->nums=A->nums; 38 // if(B->nums > 0) 39 // { 40 // int cnt=0; 41 // for(int i=1;i<=A->cols;i++) 42 // { 43 // for(int j=1;j<=A->nums;j++) 44 // { 45 // if(A->data[j].col==i) 46 // { 47 // cnt++; 48 // B->data[cnt].row=A->data[j].col; 49 // B->data[cnt].col=A->data[j].row; 50 // B->data[cnt].val=A->data[j].val; 51 // } 52 // } 53 // if(cnt == B->nums) 54 // break; 55 // } 56 // } 57 // return ; 58 //} 59 60 int num[maxn]; 61 int pos[maxn];//转置后第col行的开始位置 62 63 void Fast_TransposeTSMatrix (TSMatrix *A,TSMatrix *B) 64 { 65 B->rows=A->cols; 66 B->cols=A->rows; 67 B->nums=A->nums; 68 pos[1]=1; 69 if(B->nums) 70 { 71 memset(num,0,sizeof(num)); 72 for(int i=1;i<=A->nums;i++) 73 num[A->data[i].col]++; 74 for(int col=2;col<=A->cols;col++) 75 pos[col]=pos[col-1]+num[col-1]; 76 for(int i=1;i<=A->nums;i++) 77 { 78 int t=A->data[i].col; 79 B->data[pos[t]].row=A->data[i].col; 80 B->data[pos[t]].col=A->data[i].row; 81 B->data[pos[t]].val=A->data[i].val; 82 pos[t]++; 83 } 84 } 85 } 86 87 int main() 88 { 89 TSMatrix A,B; 90 scanf("%d %d",&A.rows,&A.cols); 91 A.nums=0; 92 for(int i=1;i<=A.rows;i++) 93 { 94 for(int j=1;j<=A.cols;j++) 95 { 96 int x; 97 scanf("%d",&x); 98 if(x) 99 { 100 A.nums++; 101 A.data[A.nums].row=i; 102 A.data[A.nums].col=j; 103 A.data[A.nums].val=x; 104 } 105 } 106 } 107 Fast_TransposeTSMatrix(&A,&B); 108 int cnt=1; 109 for(int i=1;i<=B.rows;i++)//输出转置后的矩阵 110 { 111 for(int j=1;j<=B.cols;j++) 112 { 113 int a,b; 114 a=B.data[cnt].row; 115 b=B.data[cnt].col; 116 if(a==i&&b==j) 117 { 118 printf("%d ",B.data[cnt].val); 119 cnt++; 120 } 121 else 122 printf("%d ",0); 123 } 124 printf("\n"); 125 } 126 return 0; 127 }