LeetCode48. 旋转图像

171 阅读5分钟

解题思路

这是一道找规律题,下面介绍一下解题思路:

Screen Shot 2023-02-26 at 11.49.03 AM.png

元素位置交换的规律

** 顺时针旋转矩阵,矩阵里面的元素位置交换有如下规律:**

绿色线的那一层

  1. 绿色线的那一层,以(0,0)为起点。各个元素的起点,和终点如下所示:

起点     --->    终点

(0,0)    --->  (0, 2)

(0,2) --->  (2, 2)

(2,2) --->  (2, 0)

(2, 0) --->  (0, 0)

蓝色线那一层

  1. 蓝色线那一层,以(0, 1)为起点,各个元素的起点,和终点如下所示:

起点     --->    终点

(0,1) --->  (1, 2)

(1,2) --->  (2, 1)

(2,1) --->  (1, 0)

(1, 0) --->  (0, 1)


抽象两层交换涉及的4个元素下标

两层交换涉及的4个元素下标,可以抽象为:

用Python 写出伪代码:

set n = matrix.length

i in range(n // 2):

        j in range(i, n -i -1):

即,第一层绿色线循环,起点坐标 i = 0    j = 0

    起点     --->    终点

    (0,0) --->  (0, 2)

    (0,2) --->  (2, 2)

    (2,2) --->  (2, 0)

    (2, 0) --->  (0, 0)

第二层蓝色线循环,起点坐标 i = 0    j = 1

起点     --->    终点

(0,1) --->  (1, 2)

(1,2) --->  (2, 1)

(2,1) --->  (1, 0)

(1, 0) --->  (0, 1)

两层循环交换,可以抽象为如下表达

(i, j)   -> (j, n- i - 1)

(j, n - i - 1) -> (  n-i -1, n - j -1)

(n -i -1, n -j -1) -> (n - j -1, i)

(n - j -1, i) -> (i, j)

即,总共有四个元素要交换位置(左边,和右边 各出现一次) (i, j), (j, n - i - 1), (n -i -1, n -j -1), (n - j -1, i)


每个要更新元素的下标,和要放到该位置元素的下标

上面是找到要更新元素的下标,即 (i, j), (j, n - i - 1), (n -i -1, n -j -1), (n - j -1, i)

接下来,要找到每个要更新元素,和要放到该位置元素的下标 (i, j) = ? (j, n - i - 1) = ? (n -i -1, n -j -1) = ? (n - j -1, i) = ?

第一层绿色线循环,起点坐标 i = 0    j = 0

    即将被更新的下标    --->    即将放到该位置的,新元素的原始下标

    (0,0)    =  (2, 0)

    (0,2)   =   (0, 0)

    (2,2)   =  (0, 2)

    (2, 0)  =   (2, 2)

第二层蓝色线循环,起点坐标 i = 0    j = 1

    即将被更新的下标    --->    即将放到该位置的,新元素的原始下标

    (0, 1) = (1, 0)

    (1, 2) = (0, 1)

    (2, 1) = (1, 2)

    (1, 0) =  (2, 1)

两层循环交换 被更新下标, 和即将访道该位置的,新元素的原始下标,可以抽象为如下表达
**注意: 等式右边的(i, j),各自以起始点为值:即,

  • 第一层绿线涉及的元素是相对的(i, j)取值始终是(0, 0)

  • 第二层蓝线涉及的元素是相对的(i, j)取值始终是(0, 1) **

    i, j)  =  (n - j - 1, i): e.g.  
       (00) =  (2, 0)
      (01) = (10)  
    
      (n- j - 1, i) = (n - i - 1, n - j - i): e.g.  
      (2, 0) = (2, 2)
      (10)= (21)  
    
      (n- i - 1, n- j - i) = (i,  n - j - 1): e.g.  
      (2, 2)   =  (0, 2)
      (21)   = (1, 2)  
      
      (i,  n - j - 1) = (i, j): e.g. 
      (02)   =   (0, 0)
      12) = (01

所以,用两层for 循环,来实现以两个起点(0,0)和(0,1),实现交换要第一层要,绿色线涉及的元素;和第二层,蓝色线设计的元素 以下是Python实现 示例,


 n = len(matrix)
 matrix = [[1,2,3],[4,5,6],[7,8,9]]
 for i in range(n//2):
     for j in range(i, n-i-1):
           matrix[i][j],matrix[j][n-1-i],matrix[n-1-i][n-1-j],matrix[n-1-j】[i]
           =
           matrix[n-1-j][i],matrix[i][j],matrix[j][n-1-i],matrix[n-1-i][n-1-j]


刚开始,我写成了,

       matrix[i][j],matrix[j][n-i-1],matrix[n-i-1][n-j-1],matrix[n-j-1][i]= 
         matrix[j][n-i-1],matrix[n-i-1][n-j-1],matrix[n-j-1][i],matrix[i][j] 

就是没有理解 (i,j) 上的元素是旋转后,到了(j,n - i - 1); (j,n - i - 1)上的元素是旋转后,到了 (n - i - 1, n - j -1); .... 而不是,(i,j) 上的元素 = (j,n - i - 1)上的元素;

每次交换4个元素

Screen Shot 2023-03-04 at 2.08.56 PM.png

每次交换4个元素的解决思路,也适用于其他高度的二维矩阵。

如何找到所有 子正方形的起点?###

(即上图中各个小正方形的起点)

以上图为例,矩阵长度为 4

java 代码,如何遍历 每次交换4个元素

        int n = matrix.length;
        for (int i=0; i<n/2; i++){
            for (int j=i; j < n-i-1; j++){

n为矩阵长度 = 4; 最外层for 循环 i = 0, i < n/2 = 2: j = 0, j < n-i-1 = 3 => (i,j)可取值为:(0,0), (0,1), (0,2) 最外层for 循环 i = 1, i < n/2 = 2: j = 1, j < n-i-1 = 2 => (i,j)可取值为:(1,1)

  • ( 这些就是 可以用来每次交换4个矩阵元素,所形成正方形的起点)*

交换元素位置时的小技巧:

如果交换元素时,也按照上图中,顺时针依次交换, 那就需要在每次替换掉旧位置上的元素之前,先把该位置上的值保存起来,以便下替换掉下一个元素使用;

这样,每交换4个元素,就需要创建3个临时变量,来保存即将替换掉位置上的元素。

Screen Shot 2023-03-04 at 3.14.17 PM.png

即,如上图那样顺时针交换元素 Step1. temp_1 = matrix[0][0] matrix[0][0] = matrix[3][0]

Step2. temp_2 = matrix[0][3] matrix[0][3] = temp_1

Step3. temp_3 = matrix[3][3] matrix[3][3] = temp_2

Step4.
matrix[3][0] = temp_3

如果,逆时针交换元素,就只需要保存一个临时变量,即 temp_1 = matrix[0][0] matrix[0][0] = matrix[3][0]

matrix[3][0] = matrix[3][3]

matrix[3][3] = matrix[0][3]

matrix[0][3] = temp_1

其实,总结起来 就是“先使用(该位置的值),后更新(该位置的值)”

Java 代码 实现4个元素交换位置

// 等式左边公式里的i, j 是该次4元素交换的起点值,即 (0,0),(0,1),(0,2)或(1,1)
               int temp_i_j = matrix[i][j];
                
                //[0][0] = [3][0]
                //[0][1] = [2][0]
                matrix[i][j] = matrix[n-j-1][i]; 

                // [3][0] = [3][3]
                // [2][0] = [3][2]
                matrix[n-j-1][i] =  matrix[n-i-1][n-j-1];

                // [3][3] = [0][3]
                // [3][2] = [1][3]
                matrix[n-i-1][n-j-1] = matrix[j][n-i-1];

                // [0][3] = [0][0]
                // [1][3] = [0][1]
                matrix[j][n-i-1] = temp_i_j;

参考文章

  1. # LeetCode48, 如何让矩阵原地旋转90度

  2. leetcode48** **旋转矩阵

  3. LeetCode:Rotate Image - 将矩阵顺时针旋转 90°