数组题目:旋转图像

文章目录

题目

标题和出处

标题:旋转图像

出处:48. 旋转图像

难度

5 级

题目描述

要求

给定一个 n × n \texttt{n} \times \texttt{n} n×n 的二维矩阵 matrix \texttt{matrix} matrix 表示一个图像。请你将图像顺时针旋转 90 \texttt{90} 90 度。

你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

示例

示例 1:

数组题目:旋转图像

输入: matrix   =   [[1,2,3],[4,5,6],[7,8,9]] \texttt{matrix = [[1,2,3],[4,5,6],[7,8,9]]} matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出: [[7,4,1],[8,5,2],[9,6,3]] \texttt{[[7,4,1],[8,5,2],[9,6,3]]} [[7,4,1],[8,5,2],[9,6,3]]

示例 2:

数组题目:旋转图像

输入: matrix   =   [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]] \texttt{matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]} matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]]
输出: [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]] \texttt{[[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]} [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]]

示例 3:

输入: matrix   =   [[1]] \texttt{matrix = [[1]]} matrix = [[1]]
输出: [[1]] \texttt{[[1]]} [[1]]

示例 4:

输入: matrix   =   [[1,2],[3,4]] \texttt{matrix = [[1,2],[3,4]]} matrix = [[1,2],[3,4]]
输出: [[3,1],[4,2]] \texttt{[[3,1],[4,2]]} [[3,1],[4,2]]

数据范围

  • matrix.length = n \texttt{matrix.length}=\texttt{n} matrix.length=n
  • matrix[i].length = n \texttt{matrix[i].length}=\texttt{n} matrix[i].length=n
  • 1 ≤ n ≤ 20 \texttt{1} \le \texttt{n} \le \texttt{20} 1≤n≤20
  • -1000 ≤ matrix[i][j] ≤ 1000 \texttt{-1000} \le \texttt{matrix[i][j]} \le \texttt{1000} -1000≤matrix[i][j]≤1000

解法一

思路和算法

最直观的思路是新建一个与原矩阵相同大小的临时矩阵,将图像顺时针旋转 90 90 90 度之后的值存入临时矩阵,然后将临时矩阵复制回原矩阵即可。对于边长为 n n n 的矩阵,创建临时矩阵需要 O ( n 2 ) O(n^2) O(n2) 的额外空间,虽然没有达到 O ( 1 ) O(1) O(1) 的额外空间,但是这是思考的第一步,在此基础上进行优化,即可得到使用 O ( 1 ) O(1) O(1) 额外空间的解法。

对于 0 ≤ i , j < n 0 \le i,j < n 0≤i,j<n,旋转前位于第 i i i 行第 j j j 列的元素,旋转后将位于哪一行哪一列?该元素在旋转前所在位置为从上往下第 i i i 个和从左往右第 j j j 个(均为从 0 0 0 开始计数),旋转后所在位置为从右往左第 i i i 个和从上往下第 j j j 个(均为从 0 0 0 开始计数),因此旋转后将位于第 j j j 行第 n − 1 − i n-1-i n−1−i 列。

用 ( i , j ) (i,j) (i,j) 表示第 i i i 行第 j j j 列。根据上述分析可知,经过旋转之后,位于 ( i , j ) (i,j) (i,j) 的元素旋转到 ( j , n − 1 − i ) (j,n-1-i) (j,n−1−i)。同理可得,位于 ( j , n − 1 − i ) (j,n-1-i) (j,n−1−i) 的元素旋转到 ( n − 1 − i , n − 1 − j ) (n-1-i,n-1-j) (n−1−i,n−1−j),位于 ( n − 1 − i , n − 1 − j ) (n-1-i,n-1-j) (n−1−i,n−1−j) 的元素旋转到 ( n − 1 − j , i ) (n-1-j,i) (n−1−j,i),位于 ( n − 1 − j , i ) (n-1-j,i) (n−1−j,i) 的元素旋转到 ( i , j ) (i,j) (i,j)。因此,从 ( i , j ) (i,j) (i,j) 出发,共有 4 4 4 个位置的元素进行了旋转。

例如,考虑以下 5 × 5 5 \times 5 5×5 的矩阵,其中有 4 4 4 个非零元素。

0 1 0 0 0 0 0 0 0 2 0 0 0 0 0 4 0 0 0 0 0 0 0 3 0 \begin{matrix} 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 2 \\ 0 & 0 & 0 & 0 & 0 \\ 4 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 3 & 0 \end{matrix} 00040​10000​00000​00003​02000​

在对该矩阵顺时针旋转 90 90 90 度之后,得到如下矩阵。

0 4 0 0 0 0 0 0 0 1 0 0 0 0 0 3 0 0 0 0 0 0 0 2 0 \begin{matrix} 0 & 4 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 & 0 \\ 3 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 2 & 0 \end{matrix} 00030​40000​00000​00002​01000​

从一个位置出发,共有四个位置的元素进行了旋转,这四个位置的元素可以原地修改,不需要通过新建临时矩阵完成旋转。

由于从一个位置出发可以修改四个位置的元素,因此只需要将矩阵中的四分之一的位置作为旋转的起点,即可完成整个矩阵的旋转。

选择作为旋转的起点的四分之一的位置有多种选法,原则是选中的位置中,任意两个位置都不能通过旋转得到,例如 ( i , j ) (i,j) (i,j)、 ( j , n − 1 − i ) (j,n-1-i) (j,n−1−i)、 ( n − 1 − i , n − 1 − j ) (n-1-i,n-1-j) (n−1−i,n−1−j) 和 ( n − 1 − j , i ) (n-1-j,i) (n−1−j,i) 这四个位置中有且只有一个位置被选为旋转的起点。

一种选法是选择如下集合中的全部位置: { ( i , j ) ∣ 0 ≤ i < ⌊ n 2 ⌋ , i ≤ j < n − 1 − i } \{(i,j) | 0 \le i < \Big\lfloor \dfrac{n}{2} \Big\rfloor, i \le j < n - 1 - i\} {(i,j)∣0≤i<⌊2n​⌋,i≤j<n−1−i}。注意到当 n n n 是奇数时,矩阵中心的元素不需要旋转。

以下两个矩阵分别显示 n = 4 n=4 n=4 和 n = 5 n=5 n=5 时的旋转起点和旋转规则。 A \text{A} A 表示旋转起点, A \text{A} A 的元素被旋转到 B \text{B} B, B \text{B} B 的元素被旋转到 C \text{C} C, C \text{C} C 的元素被旋转到 D \text{D} D, D \text{D} D 的元素被旋转到 A \text{A} A, X \text{X} X 表示当 n n n 是奇数时的矩阵中心的元素。

当 n = 4 n=4 n=4 时:

A A A B D A B B D D C B D C C C \begin{matrix} \text{A} & \text{A} & \text{A} & \text{B} \\ \text{D} & \text{A} & \text{B} & \text{B} \\ \text{D} & \text{D} & \text{C} & \text{B} \\ \text{D} & \text{C} & \text{C} & \text{C} \end{matrix} ADDD​AADC​ABCC​BBBC​

当 n = 5 n=5 n=5 时:

A A A A B D A A B B D D X B B D D C C B D C C C C \begin{matrix} \text{A} & \text{A} & \text{A} & \text{A} & \text{B} \\ \text{D} & \text{A} & \text{A} & \text{B} & \text{B} \\ \text{D} & \text{D} & \text{X} & \text{B} & \text{B} \\ \text{D} & \text{D} & \text{C} & \text{C} & \text{B} \\ \text{D} & \text{C} & \text{C} & \text{C} & \text{C} \end{matrix} ADDDD​AADDC​AAXCC​ABBCC​BBBBC​

代码

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        int rowEnd = n / 2;
        for (int i = 0; i < rowEnd; i++) {
            int columnEnd = n - 1 - i;
            for (int j = i; j < columnEnd; j++) {
                int temp = matrix[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] = temp;
            }
        }
    }
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是矩阵 matrix \textit{matrix} matrix 的边长。需要对矩阵中的每个元素进行旋转操作。

  • 空间复杂度: O ( 1 ) O(1) O(1)。

解法二

思路和算法

可以换一个思路。首先将矩阵转置,然后将转置后的矩阵的每一行翻转,即可实现将矩阵顺时针旋转 90 90 90 度。

用 transposed \textit{transposed} transposed 表示将 matrix \textit{matrix} matrix 转置后的矩阵,用 reversed \textit{reversed} reversed 表示将 transposed \textit{transposed} transposed 的每一行翻转后的矩阵。

对于 0 ≤ i , j < n 0 \le i,j < n 0≤i,j<n,有 transposed [ i ] [ j ] = matrix [ j ] [ i ] , reversed [ i ] [ j ] = transposed [ i ] [ n − 1 − j ] \textit{transposed}[i][j]=\textit{matrix}[j][i], \textit{reversed}[i][j]=\textit{transposed}[i][n-1-j] transposed[i][j]=matrix[j][i],reversed[i][j]=transposed[i][n−1−j]。重新整理变量名,可以得到:

matrix [ i ] [ j ] = transposed [ j ] [ i ] = reversed [ j ] [ n − 1 − i ] \textit{matrix}[i][j]=\textit{transposed}[j][i]=\textit{reversed}[j][n-1-i] matrix[i][j]=transposed[j][i]=reversed[j][n−1−i]

因此 reversed \textit{reversed} reversed 即为将 matrix \textit{matrix} matrix 顺时针旋转 90 90 90 度之后的矩阵。

转置和水平翻转操作的顺序可以交换。首先将矩阵水平翻转,然后将水平翻转的矩阵转置,也可实现将矩阵顺时针旋转 90 90 90 度。

代码

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < i; j++) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0, k = n - 1; j < k; j++, k--) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[i][k];
                matrix[i][k] = temp;
            }
        }
    }
}

复杂度分析

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),其中 n n n 是矩阵 matrix \textit{matrix} matrix 的边长。需要对矩阵中的每个元素进行转置和水平翻转操作。

  • 空间复杂度: O ( 1 ) O(1) O(1)。

上一篇:无重复字符的回文子串


下一篇:THUSC2021 Day1口胡题解