题目来自LeetCode,链接:面试题 01.07. 旋转矩阵。具体描述为:给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。不占用额外内存空间能否做到?:
示例1:
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[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]
],
原地旋转输入矩阵,使其变为:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
首先先用需要额外空间的方法,也就是用一个额外矩阵存储一个备份,然后再将旋转后矩阵给赋值到原矩阵中去。那么这个旋转之后的索引与旋转之前的索引之间的关系是咋样的呢,首先需要定义原点在矩阵的中间mid=(N-1)/2
,然后每个元素的坐标就是(i-mid, j-mid)
,利用旋转矩阵
( c o s α s i n α − s i n α c o s α ) \begin{pmatrix} cos\alpha & sin\alpha\\ -sin\alpha & cos\alpha \end{pmatrix} (cosα−sinαsinαcosα)
令 α = 90 ° \alpha=90\degree α=90°可以得到每个元素旋转后的坐标变为了
( 0 1 − 1 0 ) ⋅ ( i − m i d j − m i d ) = ( j − m i d m i d − j ) \begin{pmatrix} 0 & 1\\ -1 & 0 \end{pmatrix} \cdot \begin{pmatrix} i-mid\\ j-mid \end{pmatrix} =\begin{pmatrix} j-mid\\ mid-j \end{pmatrix} (0−110)⋅(i−midj−mid)=(j−midmid−j)
所以真正在矩阵中的索引就是
( j − m i d m i d − j ) + m i d = ( j 2 × m i d − j ) = ( j N − 1 − j ) \begin{pmatrix} j-mid\\ mid-j \end{pmatrix} +mid =\begin{pmatrix} j\\ 2\times mid-j \end{pmatrix} =\begin{pmatrix} j\\ N-1-j \end{pmatrix} (j−midmid−j)+mid=(j2×mid−j)=(jN−1−j)
这种利用额外空间的方法的时间复杂度为 O ( N 2 ) O(N^{2}) O(N2),空间复杂度为 O ( N 2 ) O(N^{2}) O(N2)。
JAVA版代码如下:
class Solution {
public void rotate(int[][] matrix) {
int N = matrix.length;
int[][] copy = new int[N][N];
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
copy[i][j] = matrix[i][j];
}
}
for (int i = 0; i < N; ++i) {
for (int j = 0; j < N; ++j) {
matrix[j][N - 1 - i] = copy[i][j];
}
}
}
}
提交结果如下:
接着是原地操作的方法,考虑到旋转过程中有四个元素是成环的,也就是第一个旋转到第二个的位置,第二个旋转到第三个的位置,第三个旋转到第四个的位置,第四个旋转到第一个的位置,如下用坐标表示更加清楚:
( i j ) → ( j N − 1 − i ) → ( N − 1 − i N − 1 − j ) → ( N − 1 − j i ) → ( i j ) \begin{pmatrix} i\\ j \end{pmatrix} \to\begin{pmatrix} j\\ N-1-i \end{pmatrix} \to\begin{pmatrix} N-1-i\\ N-1-j \end{pmatrix} \to\begin{pmatrix} N-1-j\\ i \end{pmatrix} \to\begin{pmatrix} i\\ j \end{pmatrix} (ij)→(jN−1−i)→(N−1−iN−1−j)→(N−1−ji)→(ij)
所以可以一次到位完成这四个元素的旋转,从而可以原地操作(需要注意的是只需要遍历四分之一的(i,j)
)。时间复杂度还是 O ( N 2 ) O(N^{2}) O(N2),空间复杂度则降为 O ( 1 ) O(1) O(1)。
JAVA版代码如下:
class Solution {
public void rotate(int[][] matrix) {
int N = matrix.length;
for (int i = 0; i <= (N - 1) / 2; ++i) {
for (int j = 0; j < N / 2; ++j) {
int temp = 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] = matrix[i][j];
matrix[i][j] = temp;
}
}
}
}
提交结果如下:
Python版代码如下:
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
N = len(matrix)
for i in range((N + 1) // 2):
for j in range(N // 2):
temp = 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] = matrix[i][j]
matrix[i][j] = temp
提交结果如下: