矩阵原地转置算法

        这个面试题要求现场写程序。

        题目描述非常简单:给一个m*n的矩阵,要求实现矩阵的转置,不要额外开辟内存空间。

        题目分析:

        如果可以开辟额外内存空间,我们可以申请一个n*m的矩阵,然后挨个将矩阵元素放入转置之后的位置。但题目明确指出不能开辟额外内存空间,显然,最简单的实现就是,使用一个一维数组保存原来的矩阵(行优先,即一行一行的保存矩阵,其实在各个编译器中,这是最常见的保存方式),然后在这个一维数组上实现元素位置的变化。变化后,一行一行的读取一维数组时,读出的即是转置后的矩阵的行。下面举个栗子,看这个图,介绍了整个过程:

矩阵原地转置-小米面试题 - 小屈 - 屈世超的博客

 初始矩阵如下


这个矩阵中,每个数字的下标表示这个数字在一维数组中的保存序号。经过转置后,矩阵应该如下:


        同样的,每个数字的下标表示这个数字在一维数组中保存的序号。虽然形象来看,转置后,矩阵应该这个样子,可是仍然按照一维数组保存和表示。大家脑海里应该都有这个影像。

        通过这个栗子,我们可以看到,实际上,我们只需要对这个一维数组中的元素进行交换,即可达到目的。可是该怎么变换呢?

        我们先来看看这些数字的位置是如何变化的。下面我们先把位置变化列举出来:

                                                                        0  to 0       5 to 1       10 to 2

                                                                        1  to 3       6 to 4        11 to 5

                                                                        2   to 6      7 to 7        12 to 8

                                                                        3   to 9      8 to 10      13 to 11

                                                                        4   to 12    9 to 13       14 to 14

        猛一看,这不是稍微有点规律吗!直接将0、1、2、3、4的数字分别移动到0、3、6、9、12,将5、6、7、8、9分别移动1、4、7、10、13,将10、11、12、13、14移动到2、5、8、11、14不就可以吗?但是存在一个问题,将1位置的数放入3位置,这样就会覆盖3位置原来的数字,3位置的数字是需要放到9的,而也不能提前将3位置的数放到9位置,因为9位置也有数,那个数需要放到13位置的。下面我们看看这是怎么样一个放置的过程:

        1 to 3, 3 to 9, 9 to 13, 13 to 11, 11 to 5, 5 to 1

        童鞋们,发现没有经过一个循环,最后又回到了1。这说明,其实变化的过程是个循环!如果按照这个顺序变化,肯定是不可以的了,因为要将1中的数组放到3,必须先将3中数字放入9,......,循环到最后,发现必须先将1中数字放入3才能解决5中数字的放置问题。

        看到这个问题想到了什么?对!我们应该反着来,反个顺序移动!先将1中的数字放入临时变量,然后将5中的数字放入1,再将11中数字放入5,......,最后将1放入3位置。这样不就可以完美解决了吗?是的,然后再分别将2、3、4这几个数也按照刚才的流程变化位置。这样这个矩阵就完成了转置!

        下面是我自己实现的一个简单实现的代码:

public class App {
 public void rotateArray(int[] array, int rowNum, int columnNum) {
  int maxValue = -1;
  for (int i = 0; i < array.length; i++){
   if (maxValue < array[i]){
    maxValue = array[i];
   }
  }
  maxValue += 1;

  for (int i = 0; i < array.length; i++) {
   if(array[i] >= maxValue){
    continue;
   }
   int rowOri = i / columnNum, columnOri = i % rowNum;
   if (rowOri == columnOri) {
    array[i] += maxValue;
    continue;
   }

   int startRow = columnOri, startColumn = rowOri;
   int rowMoveTo = columnOri, columnMoveTo = rowOri;
   int newColumnNum = rowNum;
   int moveValue = array[i];
   
   while(rowMoveTo != startColumn || columnMoveTo != startRow){
    int offset = rowMoveTo * newColumnNum + columnMoveTo;
    int tmpValue = array[offset];
    array[offset] = moveValue;
    array[offset] += maxValue;
   
    rowOri = offset / columnNum;
    columnOri = offset % columnNum;
    rowMoveTo = columnOri;
    columnMoveTo = rowOri;
    moveValue = tmpValue;
   }
   array[i] = moveValue + maxValue;
  }
 }
}


  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值