【LeetCode 热题 100】48. 旋转图像——转置+水平翻转

Problem: 48. 旋转图像

题目:给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。

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

文章目录

整体思路

这段代码旨在解决一个经典的矩阵问题:旋转图像 (Rotate Image) 。问题要求将一个 N x N 的二维矩阵顺时针旋转 90 度 ,并且要求是原地完成,即不使用额外的矩阵空间。

该算法采用了一种非常经典且优雅的 "两步变换法" 来实现原地旋转。它将复杂的旋转操作分解为两个更简单、更直观的线性代数变换:转置水平翻转

  1. 第一步:沿主对角线转置矩阵

    • 什么是转置 :矩阵的转置操作就是将其行和列互换。元素 matrix[i][j] 会和 matrix[j][i] 交换位置。
    • 实现方式 :代码通过一个嵌套循环来实现。外层循环 for (int i = 0; i < m; i++) 遍历每一行,内层循环 for (int j = 0; j < i; j++) 只遍历主对角线左下方的元素。
    • 为什么 j < i :这个条件是关键,它确保了每个位于对角线一侧的元素 (i, j) 只与其对应的 (j, i) 交换一次。如果 j 遍历到 m-1,那么每个元素会被交换两次,最终导致矩阵变回原样。
    • 效果 :完成这一步后,原始矩阵 [[1,2,3],[4,5,6],[7,8,9]] 会变成 [[1,4,7],[2,5,8],[3,6,9]]
  2. 第二步:水平翻转矩阵的每一行

    • 什么是水平翻转:对矩阵的每一行进行独立的反转操作。
    • 实现方式 :代码通过一个外层 for-each 循环遍历矩阵的每一行 row。对于每一行,再通过一个内层循环 for (int i = 0; i < row.length / 2; i++) 来实现该行的原地反转。这个内层循环使用经典的双指针思想(一个从头开始,一个从尾开始)来交换元素。
    • 效果 :对上一步转置后的矩阵 [[1,4,7],[2,5,8],[3,6,9]] 的每一行进行翻转,会得到:
      • [1,4,7] -> [7,4,1]
      • [2,5,8] -> [8,5,2]
      • [3,6,9] -> [9,6,3]
    • 最终的矩阵就是 [[7,4,1],[8,5,2],[9,6,3]],这正是原始矩阵顺时针旋转 90 度的结果。

通过将旋转分解为"转置 + 翻转"这两个步骤,算法巧妙地实现了原地旋转,逻辑清晰且易于理解。

完整代码

java 复制代码
class Solution {
    /**
     * 将一个 N x N 的二维矩阵原地顺时针旋转 90 度。
     * @param matrix 要旋转的 N x N 矩阵
     */
    public void rotate(int[][] matrix) {
        // 获取矩阵的维度(N x N,所以 m 即为 N)
        int m = matrix.length;
        
        // 步骤 1: 沿主对角线转置矩阵
        // 即 matrix[i][j] 与 matrix[j][i] 交换
        for (int i = 0; i < m; i++) {
            // 内层循环 j < i 是关键,确保每个元素只交换一次
            for (int j = 0; j < i; j++) {
                // 执行交换
                int temp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = temp;
            }
        }
        
        // 步骤 2: 水平翻转矩阵的每一行
        // 遍历矩阵的每一行
        for (int[] row : matrix) {
            // 使用双指针法原地反转当前行
            // i 从行首开始,m - 1 - i 从行尾开始
            for (int i = 0; i < row.length / 2; i++) {
                // 执行交换
                int temp = row[i];
                row[i] = row[m - 1 - i];
                row[m - 1 - i] = temp;
            }
        }
    }
}

时空复杂度

时间复杂度:O(N^2)

  1. 转置操作
    • 第一个双层 for 循环的迭代次数是 1 + 2 + ... + (N-1),即 N * (N-1) / 2
    • 因此,这部分的时间复杂度是 O(N^2)
  2. 翻转操作
    • 第二个双层 for 循环,外层循环执行 N 次(遍历每一行)。
    • 内层循环对每一行执行 N/2 次交换操作。
    • 总的操作次数约为 N * (N/2)
    • 因此,这部分的时间复杂度也是 O(N^2)

综合分析

算法的总时间复杂度是 O(N^2) + O(N^2) = O(N^2)。这是不可避免的,因为我们至少需要访问矩阵中的每一个元素一次。

空间复杂度:O(1)

  1. 主要存储开销 :该算法直接在输入的 matrix 数组上进行修改,没有创建任何新的、与输入规模 N 成比例的数据结构。
  2. 辅助变量 :只使用了 m, i, j, temprow 等几个常数数量的变量。

综合分析

算法所需的额外辅助空间是常数级别的。因此,其空间复杂度为 O(1),完全满足了原地旋转的要求。

参考灵神

建议看一下灵神题解太强了

相关推荐
身如柳絮随风扬1 小时前
Java中的CAS机制详解
java·开发语言
-dzk-2 小时前
【代码随想录】LC 59.螺旋矩阵 II
c++·线性代数·算法·矩阵·模拟
风筝在晴天搁浅3 小时前
hot100 78.子集
java·算法
Jasmine_llq3 小时前
《P4587 [FJOI2016] 神秘数》
算法·倍增思想·稀疏表(st 表)·前缀和数组(解决静态区间和查询·st表核心实现高效预处理和查询·预处理优化(提前计算所需信息·快速io提升大规模数据读写效率
超级大只老咪3 小时前
快速进制转换
笔记·算法
m0_706653233 小时前
C++编译期数组操作
开发语言·c++·算法
故事和你913 小时前
sdut-Java面向对象-06 继承和多态、抽象类和接口(函数题:10-18题)
java·开发语言·算法·面向对象·基础语法·继承和多态·抽象类和接口
qq_423233904 小时前
C++与Python混合编程实战
开发语言·c++·算法
TracyCoder1234 小时前
LeetCode Hot100(19/100)——206. 反转链表
算法·leetcode
m0_715575344 小时前
分布式任务调度系统
开发语言·c++·算法