数据结构25:矩阵转置算法(三元组顺序表)
矩阵的转置实际上就是将数据元素的行标和列标互换,即 T(i,j) = M(j,i) 。例如:
图1 矩阵的转置
相应地,三元组表转变为:
图2 三元组表
矩阵的转置,经历了三个步骤:- 矩阵的行数 n 和列数 m 的值交换;
- 将三元组中的i和j调换;
- 转换之后的表同样按照行序(置换前的列序)为主序,进行排序;
普通算法
普通算法的实现过程为:- 将矩阵的行数和列数进行调换;
- 遍历表 a 的 j 列(查找 j 的值,从 1 一直到未转置之前的矩阵的列数 m ),遍历的过程,就可以自动存储为表 b 的形式。
因为在表 a 中 i 列的数值是从小到大的,在根据 j 列由上到下的遍历时, i 列同样也是有序的。
实现代码:
TSMatrix transposeMatrix(TSMatrix M, TSMatrix T)
{ //行和列置换 T.m = M.n; T.n = M.m; T.num = M.num; if (T.num)
{ int q = 0; //依次遍历M矩阵的列(从1开始),的遍历的过程中将行标和列标置换,得到置换后的三元表T for (int col=1; col<=M.m; col++)
{ for (int p=0; p<M.num; p++)
{ if (M.data[p].j == col)
{ T.data[q].i = M.data[p].j; T.data[q].j = M.data[p].i; T.data[q].data = M.data[p].data; q++; } } } }
return T; }
O(m*num)
,和矩阵的列数以及非 0 元素的个数的乘积成正比,如果稀疏矩阵的非 0 元素很多的情况,使用这个算法,虽然一定程度上节省了空间,但是时间复杂度会很高。
快速转置算法
快速转置算法在普通算法的基础上,对遍历存储的过程做了改进。首先将每一列中非 0 元素的个数对应地存储在一个数组(数组名为array)中。在此基础上,计算出每一列第一个元素存放在三元组表中的位置,存储在数组(数组名为 cpot )中。
第一列第一个非 0 元素肯定存放在第一个位置,第二列第一个非 0 元素的位置 = 第一列存放的起始位置 + 第一列的非 0 元素个数,以此类推。
用图 2 中置换之前的表举例:
array 数组中的数据表示,第一列有一个非 0 元素,第二列中 3 个非0元素。
cpot 数组中的数据表示,第一列中第一个数据存储的位置默认是 1 ,第二列第一个非 0 元素存放的位置是 2。
计算方法是:cpot[col] = cpot[col-1] + array[col-1],即后边一列第一个非 0 元素存放的位置为前边一列第一个非 0 元素存放的位置加上该列非 0 元素的个数的和。
在以上两个数组的基础上,当遍历表 a 的 j 列时,根据每个元素 j 列的数值,就可以判断出它在表 b 中的存放位置,整个三元组表只需要遍历一次,就能实现矩阵的转置。实现代码:
TSMatrix fastTransposeMatrix(TSMatrix M, TSMatrix T)
{ //行和列置换 T.m = M.n; T.n = M.m; T.num = M.num; if (T.num)
{ //创建并初始化array数组 int array[number]; for (int col=1; col<=M.m; col++)
{ array[col]=0; } for (int t=0; t<M.num; t++)
{ int j = M.data[t].j; array[j]++; }
//创建并初始化cpot数组 int cpot[T.m+1]; cpot[1]=1; //第一列中第一个非0元素的位置默认为1 for (int col=2; col<=M.m; col++)
{ cpot[col] = cpot[col-1]+array[col-1]; }
for (int p=0; p<M.num; p++)
{ //提取当前三元组的列数 int col = M.data[p].j; //根据列数和cpot数组,找到当前元素需要存放的位置 int q = cpot[col]; //转置矩阵的三元组默认从数组下标0开始,而得到的q值是单纯的位置,所以要减1 T.data[q-1].i = M.data[p].j; T.data[q-1].j = M.data[p].i; T.data[q-1].data = M.data[p].data; //存放完成后,cpot数组对应的位置要+1,以便下次该列存储下一个三元组 cpot[col]++; } }
return T; }
O(m+num)
(实际得到的是O(2*m+2*num)
,当 m 和 num 足够大时,可以省略常数参数),即使最坏情况下,矩阵中的元素都是非 0 元素,时间负责度为O(m*n)
。称此算法为快速转置算法。
两种算法的完整代码
1 #include<stdio.h> 2 #define number 10
3 typedef struct
{ 4 int i,j; 5 int data; 6 }triple;
7 typedef struct
{ 8 triple data[number]; 9 int rpos[number]; 10 int n,m,num; 11 }TSMatrix;
12 TSMatrix transposeMatrix(TSMatrix M,TSMatrix T)
{ 13 T.m = M.n; 14 T.n = M.m; 15 T.num = M.num;
16 if (T.num)
{ 17 int q = 0; 18 for (int col=1; col<=M.m; col++)
{ 19 for (int p=0; p<M.num; p++)
{ 20 if (M.data[p].j == col)
{ 21 T.data[q].i = M.data[p].j; 22 T.data[q].j = M.data[p].i; 23 T.data[q].data = M.data[p].data; 24 q++; 25 } 26 } 27 } 28 }
29 return T; 30 }
31 TSMatrix fastTransposeMatrix(TSMatrix M, TSMatrix T)
{ 32 T.m = M.n; 33 T.n = M.m; 34 T.num = M.num; 35 if (T.num)
{ 36 int array[number]; 37 for (int col=1; col<=M.m; col++)
{ 38 array[col] = 0; 39 } 40 for (int t=0; t<M.num; t++)
{ 41 int j = M.data[t].j; 42 array[j]++; 43 } 44 int cpot[T.m+1]; 45 cpot[1] = 1; 46 for (int col=2; col<=M.m; col++)
{ 47 cpot[col] = cpot[col-1]+array[col-1]; 48 } 49 for (int p=0; p<M.num; p++)
{ 50 int col = M.data[p].j; 51 int q = cpot[col]; 52 T.data[q-1].i = M.data[p].j; 53 T.data[q-1].j = M.data[p].i; 54 T.data[q-1].data = M.data[p].data; 55 cpot[col]++; 56 } 57 } 58
return T; 59 }
60 int main()
{ 61 TSMatrix M; 62 M.m = 2; 63 M.n = 3; 64 M.num = 4; 65 M.data[0].i = 1; 66 M.data[0].j = 2; 67 M.data[0].data = 1; 68 M.data[1].i = 2; 69 M.data[1].j = 2; 70 M.data[1].data = 3; 71 M.data[2].i = 3; 72 M.data[2].j = 1; 73 M.data[2].data = 6; 74 M.data[3].i = 3; 75 M.data[3].j = 2; 76 M.data[3].data = 5; 77 TSMatrix T; 78 T=transposeMatrix(M, T); 79 printf("使用普通方法:\n"); 80 for (int i=0; i<T.num; i++)
{ 81 printf("(%d,%d,%d)", T.data[i].i, T.data[i].j, T.data[i].data); 82 } 83 printf("\n"); 84 TSMatrix T1; 85 T1 = fastTransposeMatrix(M, T1); 86 printf("使用改进方法:\n"); 87 for (int i=0; i<T.num; i++)
{ 88 printf("(%d,%d,%d)", T.data[i].i, T.data[i].j, T.data[i].data); 89 }
90 return 0; 91 }
输出结果
使用普通方法:
(1,3,6)(2,1,1)(2,2,3)(2,3,5)
使用改进方法:
(1,3,6)(2,1,1)(2,2,3)(2,3,5)
(1,3,6)(2,1,1)(2,2,3)(2,3,5)
使用改进方法:
(1,3,6)(2,1,1)(2,2,3)(2,3,5)