LeetCode 48:旋转矩阵

1. 题目

给你一幅由 N × N 矩阵表示的图像,其中每个像素的大小为 4 字节。请你设计一种算法,将图像旋转 90 度。

不占用额外内存空间能否做到?

 

2. 示例

给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]

给定 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]
]
 

3. 解题思路

3.1 方法一:使用辅助数组

矩阵旋转的特点:对于矩阵中第 i i ii ii 行的第 j j jj jj 个元素,在旋转后,它出现在倒数第 i i ii ii 列的第 j j jj jj 个位置。

m a t r i x [ r o w ] [ c o l ] matrix[row][col] matrix[row][col],在旋转后,它的新位置为 m a t i r x n e w [ c o l ] [ n − r o w − 1 ] matirx_{new}[col][n-row-1] matirxnew[col][nrow1]
即: m a t r i x n e w [ c o l ] [ n − r o w − 1 ] = m a t r i x [ r o w ] [ c o l ] matrix_{new}[col][n - row - 1] = matrix[row][col] matrixnew[col][nrow1]=matrix[row][col]

3.2 方法二:原地旋转

3.2.1 如何原地旋转

方法一占用额外内存的原因:
m a t r i x [ c o l ] [ n − r o w − 1 ] = m a t r i x [ r o w ] [ c o l ] matrix[col][n - row - 1] = matrix[row][col] matrix[col][nrow1]=matrix[row][col] 操作会覆盖原始矩阵的值,所以需要创建一个相同大小的辅助矩阵

解决办法: 用一个临时变量 t e m p temp temp 暂存 m a t r i x [ c o l ] [ n − r o w − 1 ] matrix[col][n - row - 1] matrix[col][nrow1] 的值

此时 t e m p temp temp 中保存的 m a t r i x [ c o l ] [ n − r o w − 1 ] matrix[col][n - row - 1] matrix[col][nrow1] 的值就可以更新至 m a t i r x [ n − r o w − 1 ] [ n − c o l − 1 ] matirx[n - row - 1][n - col - 1] matirx[nrow1][ncol1]
即: m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] = m a t r i x [ c o l ] [ n − r o w − 1 ] matrix[n - row - 1][n - col - 1]= matrix[col][n - row - 1] matrix[nrow1][ncol1]=matrix[col][nrow1]

综上:
【注意:考虑第一次直接赋值】
t e m p = m a t r i x [ c o l ] [ n − r o w − 1 ] temp = matrix[col][n - row - 1] temp=matrix[col][nrow1]
m a t r i x [ c o l ] [ n − r o w − 1 ] = m a t r i x [ r o w ] [ c o l ] matrix[col][n - row - 1] = matrix[row][col] matrix[col][nrow1]=matrix[row][col]


同样地,直接赋值会覆盖掉 m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] matrix[n - row - 1][n - col - 1] matrix[nrow1][ncol1] 原来的值
解决办法:再次用临时变量 t e m p temp temp 暂存 m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] matrix[n - row - 1][n - col - 1] matrix[nrow1][ncol1] 的值

此时 t e m p temp temp 中保存的 m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] matrix[n−row−1][n−col−1] matrix[nrow1][ncol1] 的值就可以更新至 m a t r i x [ n − c o l − 1 ] [ n − ( n − r o w − 1 ) − 1 ] = m a t r i x [ n − r o l − 1 ] [ r o w ] matrix[n-col-1][n-(n-row-1)-1]=matrix[n-rol-1][row] matrix[ncol1][n(nrow1)1]=matrix[nrol1][row]
即: m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] = m a t r i x [ c o l ] [ n − r o w − 1 ] matrix[n - row - 1][n - col - 1]= matrix[col][n - row - 1] matrix[nrow1][ncol1]=matrix[col][nrow1]

综上:
【注意:考虑第二次直接赋值】
t e m p = m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] temp = matrix[n - row - 1][n - col - 1] temp=matrix[nrow1][ncol1]
m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] = m a t r i x [ c o l ] [ n − r o w − 1 ] matrix[n−row−1][n−col−1]=matrix[col][n−row−1] matrix[nrow1][ncol1]=matrix[col][nrow1]
m a t r i x [ c o l ] [ n − r o w − 1 ] = m a t r i x [ r o w ] [ c o l ] matrix[col][n - row - 1] = matrix[row][col] matrix[col][nrow1]=matrix[row][col]


同样地,直接赋值会覆盖掉 m a t r i x [ n − c o l − 1 ] [ r o w ] matrix[n-col-1][row] matrix[ncol1][row] 原来的值
解决办法:再次用临时变量 t e m p temp temp 暂存 m a t r i x [ n − c o l − 1 ] [ r o w ] matrix[n-col-1][row] matrix[ncol1][row] 的值

此时 t e m p temp temp 中保存的 m a t r i x [ n − c o l − 1 ] [ r o w ] matrix[n-col-1][row] matrix[ncol1][row] 的值就可以更新至 m a t r i x [ r o w ] [ n − ( n − c o l − 1 ) − 1 ] = m a t r i x [ r o w ] [ c o l ] matrix[row][n-(n-col-1)-1]=matrix[row][col] matrix[row][n(ncol1)1]=matrix[row][col]
即: m a t r i x [ r o w ] [ c o l ] = m a t r i x [ n − c o l − 1 ] [ r o w ] ] matrix[row][col]= matrix[n-col-1][row]] matrix[row][col]=matrix[ncol1][row]]

综上:
【注意:考虑第三次直接赋值,回到了最初的起点,实现了闭环!!!】
t e m p = m a t r i x [ n − c o l − 1 ] [ r o w ] temp = matrix[n-col-1][row] temp=matrix[ncol1][row]
m a t r i x [ n − c o l − 1 ] [ r o w ] = m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] matrix[n-col-1][row]= matrix[n - row - 1][n - col - 1] matrix[ncol1][row]=matrix[nrow1][ncol1]
m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] = m a t r i x [ c o l ] [ n − r o w − 1 ] matrix[n−row−1][n−col−1]=matrix[col][n−row−1] matrix[nrow1][ncol1]=matrix[col][nrow1]
m a t r i x [ c o l ] [ n − r o w − 1 ] = m a t r i x [ r o w ] [ c o l ] matrix[col][n - row - 1] = matrix[row][col] matrix[col][nrow1]=matrix[row][col]
m a t r i x [ r o w ] [ c o l ] = t e m p matrix[row][col] = temp matrix[row][col]=temp

在这里插入图片描述
在这里插入图片描述

即:
m a t r i x [ r o w ] [ c o l ] = m a t r i x [ n − c o l − 1 ] [ r o w ] matrix[row][col] = matrix[n-col-1][row] matrix[row][col]=matrix[ncol1][row]
m a t r i x [ n − c o l − 1 ] [ r o w ] = m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] matrix[n-col-1][row]= matrix[n - row - 1][n - col - 1] matrix[ncol1][row]=matrix[nrow1][ncol1]
m a t r i x [ n − r o w − 1 ] [ n − c o l − 1 ] = m a t r i x [ c o l ] [ n − r o w − 1 ] matrix[n−row−1][n−col−1]=matrix[col][n−row−1] matrix[nrow1][ncol1]=matrix[col][nrow1]
m a t r i x [ c o l ] [ n − r o w − 1 ] = m a t r i x [ r o w ] [ c o l ] matrix[col][n - row - 1] = matrix[row][col] matrix[col][nrow1]=matrix[row][col]

3.2.2 哪些位置需要原地旋转

情况一:当 n n n 为偶数时,我们需要枚举 n 2 / 4 = ( n / 2 ) × ( n / 2 ) n^2 / 4 = (n/2)×(n/2) n2/4=(n/2)×(n/2) 个位置,可以将该图形分为四块
在这里插入图片描述
 
情况二:当 n为奇数时,由于中心的位置经过旋转后位置不变,我们需要枚举 ( n 2 − 1 ) / 4 = ( ( n − 1 ) / 2 ) × ( ( n + 1 ) / 2 ) (n^2-1) / 4 = ((n−1)/2)×((n+1)/2) (n21)/4=((n1)/2)×((n+1)/2)个位置,需要换一种划分的方式

在这里插入图片描述

3.3 方法三:用翻转代替旋转

先将其通过水平轴翻转得到:
在这里插入图片描述
再根据主对角线翻转得到:
在这里插入图片描述
对于水平轴翻转而言,我们只需要枚举矩阵上半部分的元素,和下半部分的元素进行交换,即
在这里插入图片描述
对于主对角线翻转而言,我们只需要枚举对角线左侧的元素,和右侧的元素进行交换,即
在这里插入图片描述
将它们联立即可得到:
在这里插入图片描述

 

4. 提交代码

4.1 方法一:使用辅助数组(占用了额外空间)

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        N = len(matrix)
        matrix_new = [[0] * N for _ in range(N)] # 创建辅助矩阵
        for i in range(N):
            for j in range(N):
                matrix_new[j][N - i - 1] = matrix[i][j]
        matrix[:] = matrix_new

4.2 方法二:原地旋转

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        n = len(matrix)
        for i in range(n // 2):
            for j in range((n + 1) // 2):# 注意这一步包含了n为奇数和偶数两种情况
                matrix[i][j], matrix[n - j - 1][i], matrix[n - i - 1][n - j - 1], matrix[j][n - i - 1] \
                    = matrix[n - j - 1][i], matrix[n - i - 1][n - j - 1], matrix[j][n - i - 1], matrix[i][j]


4.3 方法三:用翻转代替旋转

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        n = len(matrix)
        # 水平翻转
        for i in range(n // 2):
            for j in range(n):
                matrix[i][j], matrix[n - i - 1][j] = matrix[n - i - 1][j], matrix[i][j]
        # 主对角线翻转
        for i in range(n):
            for j in range(i):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

 

5. 复杂度分析

5.1 方法一:使用辅助数组

时间复杂度: O ( N 2 ) O(N^2) O(N2),其中 N N N m a t r i x matrix matrix 的边长。
空间复杂度: O ( N 2 ) O(N^2) O(N2)。我们需要使用一个和 m a t r i x matrix matrix 大小相同的辅助数组。

5.2 方法二:原地旋转

时间复杂度 O ( N 2 ) O(N^2) O(N2), 其中 N N N m a t r i x matrix matrix 的边长。我们需要枚举的子矩阵大小为 O ( N 2 ) ⌊ n / 2 ⌋ × ⌊ ( n + 1 ) / 2 ⌋ ) = O ( N 2 ) O(N^2)⌊n/2⌋×⌊(n+1)/2⌋)=O(N^2) O(N2)n/2×(n+1)/2)=O(N2)
空间复杂度 O ( 1 ) O(1) O(1),为原地旋转。

5.3 方法三:用翻转代替旋转

时间复杂度 O ( N 2 ) O(N^2) O(N2), 其中 N N N m a t r i x matrix matrix 的边长。对于每一次翻转操作,我们都需要枚举矩阵中一半的元素。
空间复杂度 O ( 1 ) O(1) O(1),为原地旋转。

 

6. 总结

深入理解python赋值

6.1 引用

matrix = matrix_new # 实际上是引用
  • 函数里的matrix不再是实参的地址了,而是matrix_new的一个引用,即使修改了,也不会影响实参。

6.2 浅复制

matrix[:] = matrix_new # 修改原来内存中的值
  • 在调用函数时,传递给形参的是实参matrix的地址,用切片复制的方式可以根据这个地址对实参进行原地修改
  • 当列表对象有嵌套的时候也会产生出乎意料的错误
a = [0, [1, 2], 3]
b = a[:]
a[0] = 8
a[1][1] = 9
print(a)
print(b)

代码运行结果:

[8, [1, 9], 3]
[0, [1, 9], 3]

b 的第二个元素也被改变
在这里插入图片描述

6.3 深复制

import copy

a = [0, [1, 2], 3]
b = copy.deepcopy(a)
a[0] = 8
a[1][1] = 9
print(a)
print(b)

代码运行结果:

[8, [1, 9], 3]
[0, [1, 2], 3]

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值