当前位置:   article > 正文

数组以及稀疏矩阵的快速转置算法详细分析

数组以及稀疏矩阵的快速转置算法详细分析

一.数组:

1.数组的地址计算:

数组的地址计算比较简单,读者可以自行了解,在这里不再赘述;

2.特殊矩阵的压缩存储:

 在这里我们主要说明稀疏矩阵的主要内容:

(1)稀疏矩阵的三元组表示法:

在这里,存放非零元素的行标,列标以及元素值:

三元组表存放在一维数组中,一行一行存储,并且在每一行中,按照列号递增的方式来 !

(2)稀疏矩阵的三元组表的定义:

  1. #define MAXSIZE 1000
  2. #define ElementType int
  3. typedef struct
  4. {
  5. int row, col;//定义行标,列标
  6. ElementType e;
  7. }Triple;
  8. typedef struct//定义矩阵
  9. {
  10. Triple data[MAXSIZE];
  11. int m, n, len;//矩阵的行,列数,以及非零元素的个数;
  12. }TSmatrix;

 (3)矩阵的转置算法

a.经典的矩阵转置:
  1. //经典的矩阵转置算法:就是给出矩阵及其转置矩阵,对应位置直接赋值
  2. void TransMatrix(ElementType source[m][n], ElementType dest[n][m])(二维的)
  3. {
  4. int i, j;
  5. for (i = 0; i < m; i++)
  6. {
  7. for (j = 0; j < n; j++)
  8. {
  9. dest[j][i] = source[i][j];
  10. }
  11. }
  12. }
b.用三元组实现稀疏矩阵的转置:

关键点:保证转置之后的三元组也是按照行序优先来进行存储的

那么如果说先直接交换行列的值,那么就会造成转换之后的顺序紊乱,如果要保证转置之后仍然按照行优先来进行存储,那么我们需要对其按照行优先进行排序,就会移动大量的元素;

为了解决上述问题:

(1)列序递增转置算法

假设A为要转置的三元组表,B为最终结果的存放;

列序递增即按照A的列号进行递增排列存储的算法;

依次扫描A的列,每一个分别找出列号为1-K的,找到后就存入B中;

在这里:可能会有读者有这样的疑问:我们已经按照A的列号进行递增存贮,我们是否还需要对A的行号进行比较判断呢?以保证转置之后的的元素按照“列序”递增的顺序进行排列

答案是否定的,在转置之前,A就是按照行号递增来进行排序的,那么我们一定是先找到行号比较小的符合条件的元素,所以不需要再对行号进行排序;

  1. //稀疏矩阵的列序递增转置算法
  2. void TransposeTSMatrix(TSmatrix A, TSmatrix* B)//B传址调用
  3. {
  4. int i, j, k;
  5. //先对B的行列数,以及元素个数进行初始化
  6. B->m = A.n;
  7. B->n = A.m;
  8. B->len = A.len;
  9. if (B->len > 0)
  10. {
  11. j = 0;
  12. for (k = 1; k <= A.n; k++)//扫描A,找到列数等于K的元素
  13. {
  14. for (i = 1; i < A.len; i++)//对三元组表中所有的元素进行遍历
  15. {
  16. if (A.data[i].col == k)
  17. {
  18. B->data[j].row = A.data[i].col;
  19. B->data[j].col = A.data[i].row;
  20. B->data[j].e = A.data[i].e;
  21. j++;
  22. }
  23. }
  24. }
  25. }
  26. }

算法的时间复杂度分析:算法的时间耗费主要是在双重循环中,时间复杂度为:O(A.n*A.len)

A.len最大为A.m*A.n,此时算法时间复杂度为:O(A.m*A.n^2),而经典的算法实现转置的时间复杂度为:O(A.m*A.n)

由此可见:采用列序递增的算法,储存空间减少,但是在非零元素的个数较多时,时间复杂度并未显著降低;

(2)一次定位快速转置算法

一次定位快速转置:为了降低上述过程的时间复杂度;

那么我们需要以牺牲空间为代价:引入num[],和position[]这两个数组

其中num[]用于存放每一列的非零元素的总个数,position数组用于存放每一列的第一个非零元素对应在B中应该存放的位置,即在B三元组中的下标;

num[col]:扫描一遍三元组表A,A的元素的列标出现一次,num[col]就增加1,最终得到相应的列标对应的总的非零元素的个数;

position[col]:根据上述position其实就是表示对应元素在B中应该存放的下标的值;那么position[col]=position[col-1]+num[col-1],即上一列的下标,加上上一列的总的元素所占的位置,就是这一列的首非零元的在B中的下标;

在算法实现中:我们首先将num初始化为0,并且遍历一次A,求出相应的num数组;

其次我们应该利用:position[col]=position[col-1]+num[col-1],这个语句来计算列对应的position的值,并且由于存在col-1,而col是从1开始的,而第一个存储的元素的在B中的下标一定为1,那么我们可以直接将position[1]=1即可;然后就可以从A的列标为2,开始遍历,计算position[col];在这里是给position赋值,所以不用初始化为0;

关于B中的对应的下标的移动:在不同的列中,是通过position来进行增加移动的;

那在相同的列中呢?我们可以在相同的列中,每存储到B中一个元素,就把对应的列position[col]++,然后就可以使得position[col]一直都指向即将要存储的正确位置了;

最后,再次遍历A,将A的行列以及元素值都赋给B,每赋一个,就把对应的position[col]++,更新位置即可!(只要能够理解num和position的实现,就能够理解快速转置算法啦!

以下是具体的代码实现:

  1. void FastTransposeTSMatrix(TSmatrix A, TSmatrix *B)
  2. {
  3. int col, t, p, q;//列标
  4. int num[MAXSIZE], position[MAXSIZE];
  5. //仍然是先初始化B的行列数以及非零元素值
  6. B->len = A.len;
  7. B->n = A.m;
  8. B->m = A.n;
  9. if (B->len > 0)
  10. {
  11. for (col = 1;col <= A.n; col++)
  12. {
  13. num[col] = 0;//先将num数组初始化为0;//且数组是从1开始的!
  14. }
  15. //接着遍历A中的所有元素
  16. for (t = 1; t < A.len; t++)
  17. {
  18. num[A.data[t].col]++;//A中的元素的列标作为num的下标;
  19. }
  20. position[1] = 1;//第一个肯定是存储在第一个位置
  21. //那么从第二列开始赋值;
  22. for (col = 2; col <= A.n; col++)
  23. {
  24. position[col] = position[col - 1] + num[col - 1];
  25. }
  26. for (p = 1; p <= A.len; p++)
  27. {
  28. col = A.data[p].col;//列数取决于A中的元素的列数
  29. q = position[col];//q一直是在B中的下标;
  30. B->data[q].row = A.data[p].col;//B的行等于A的列;
  31. B->data[q].col = A.data[p].row;
  32. B->data[q].e = A.data[p].e;
  33. position[col]++;//下一个元素依然可能是这个列,因此改变下标位置;而不是这个列,对应的也有新的position值,所以....
  34. }
  35. }
  36. }

一次定位快速转置的时间复杂度:在这里时间主要耗费在4个并列的单循环上;分别为:O(A.len)+O(A.n)+O(A.len)+O(A.n),那么该算法的时间复杂度就为:O(A.n+A.len)

那么我们总结一下:按照列序递增的方式与一次定位的区别:

按照列序递增,遍历K次A,按照列号递增,每次找到对应的列号的运算,再存入到B中,也就是必须是依次存放,列号要么相等,要么相邻;

而对于一次定位快速转置来说:遍历一次,不用非要是挨着的或者是一样的列标一次存入,按照列标存入到对应的应该存放的位置,不一定非要连续存入,其实更像是插入;一次定位的关键也就在这,就是要找到列标对应的正确的存放位置,也就是上述所说的position[col]的计算;


以上就是关于稀疏矩阵快速转置的算法理解,欢迎大家在评论区进行交流!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/460016
推荐阅读
相关标签
  

闽ICP备14008679号