LeetCode每日一题

267 阅读2分钟

题目

给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。
不占用额外内存空间能否做到?

题目分析

  1. 题目中没有给出逆时针还是顺时针旋转,我们这里规定顺时针旋转90^o
  2. 本题目我使用旋转交换的方法,使用一个元素大小的额外空间
  3. 推一下元素交换后坐标的变化,官方解题或者其他博客用的是观察法,但是观察法非常容易忘,所以就推一下:
    设一个元素A的坐标是(i, j),转换为极坐标是(rcos\theta, rsin\theta),瞬时针旋转\theta_1,坐标变为(rcos(\theta-\theta_1), rsin(\theta - \theta_1)),展开后变成了(rcos\theta cos\theta_1 + rsin\theta sin\theta_1 , rsin\theta cos\theta_1 - rsin\theta_1 cos\theta), 可以看做: 系数矩阵*原坐标=新坐标
\left[\begin{matrix}cos\theta_1 & sin\theta_1\\
    -sin\theta_1 & cos\theta_1\end{matrix}\right] * \left[\begin{matrix}rcos\theta\\
    sin\theta\end{matrix}\right]

\theta_1=-90^o的时候,系数矩阵变成了

\left[\begin{matrix}0 & -1\\
    1 & 0\end{matrix}\right] * \left[\begin{matrix}rcos\theta\\
    rsin\theta\end{matrix}\right]

换回笛卡尔坐标系:

\left[\begin{matrix}0 & -1\\
    1 & 0\end{matrix}\right] * \left[\begin{matrix}i\\
    j\end{matrix}\right]

变换后得到的是(-j,i),由于代码中的索引和矩阵的元素位置不是同一形式,当坐标是负值的时候,变换为N-1-j,N-1-j再乘以负值就是j,所以变换的坐标为:

  1. (i,j) 移动到(N-1-j, i)
  2. (N-1-j, i) 移动到 (N-1-i,N-1-j)
  3. (N-1-i,N-1-j)移动到(j, N-1-i)
  4. (j, N-1-i)移动到(i,j)

代码实现

   public void rotate(int[][] matrix) {
        int n = matrix.length;
        // 外层循环遍历到中间位置,奇数边长包含中间位置。
        for (int i = 0; i < n / 2; i++) {
            // 内层循环遍历到中间位置,奇数边长不包含中间位置。
            for (int j = 0; j < (n + 1) / 2; j++) {
                int tmp = martix[i][j];
                matrix[i][j] = matrix[n-1-j][i];
                matrix[n-1-j][i] = matrix[n-1-i][n-1-j];
                matrix[n-1-i][n-1-j] = matrix[j][n-1-i];
                matrix[j][n-1-i] = tmp;
            }
        }
    }

代码分析

问题:为什么外层遍历的次数是n/2,内层的遍历次数(n+1)/2?

外层和内层的遍历次数可以互换,矩阵的元素旋转只需要考虑四 分之一,但是当边长N等于奇数的时候,无法均匀分成四部分,有一部分元素在边线上,边线的元素只能有一个边属于该部分。当n是偶数的时候,n/2和(n+1)/2相等,当n是奇数的时候,(n+1)/2比n/2大1,就是这四分之一部分包括一条边线,不包括另一条。