LeetCode 48 旋转图像 Java实现 两种方法

题目来源

48. 旋转图像


题目描述

给定一个 n x n 的二维矩阵表示一个图像,请将图像顺时针旋转 90 度。

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


题目分析

难度

  • 中等

标签

  • 矩阵
  • 数学

限制条件

  • 1 <= matrix.length == matrix[i].length <= 20
  • 1 <= n <= 20
  • -1000 <= matrix[i][j] <= 1000

解题思路

思路一:分层旋转法

通过观察可以发现,矩阵的旋转可以分解为对每一层的边界元素进行旋转。例如,对于一个 4x4 的矩阵,可以将其分为外层和内层两部分,分别处理每层的旋转。

核心算法步骤
  1. 分层遍历

    • 使用变量 i 表示当前层的左上角索引。
    • 每一层的边长为 len = n - (i << 1)(即当前层的宽度)。
    • 遍历该层的四个边界,依次交换对应位置的元素。
  2. 元素交换

    • 对于每个位置 (i, i+j),找到其对应的三个位置,并按顺序交换值。
    • 具体来说,四个位置分别为:
      • 左上角:(i, i+j)
      • 左下角:(i+len-j-1, i)
      • 右下角:(i+len-1, i+len-j-1)
      • 右上角:(i+j, i+len-1)
  3. 逐层收缩

    • 处理完当前层后,进入下一层(i++),直到所有层都处理完毕。

思路二:数学变换法

通过数学推导可以发现,顺时针旋转 90 度等价于以下两个操作的组合:

  1. 沿主对角线翻转:将矩阵沿主对角线对称翻转。
  2. 上下翻转:将矩阵沿水平中轴翻转。

这种方法的优势在于逻辑清晰,代码实现简单。


代码实现

方法一:分层旋转法

/**
 * 48. 旋转图像
 * @param matrix 二维矩阵
 */
public void rotate(int[][] matrix) {
    int n = matrix.length;

    for (int i = 0; i <= (n >> 1); i++) { // 分层遍历
        int len = n - (i << 1); // 当前层的边长
        for (int j = 0; j < len - 1; j++) { // 遍历当前层的四个边界
            int temp = matrix[i][i + j];
            matrix[i][i + j] = matrix[i + len - j - 1][i];
            matrix[i + len - j - 1][i] = matrix[i + len - 1][i + len - j - 1];
            matrix[i + len - 1][i + len - j - 1] = matrix[i + j][i + len - 1];
            matrix[i + j][i + len - 1] = temp;
        }
    }
}

方法二:数学变换法

/**
 * 48. 旋转图像
 * @param matrix 二维矩阵
 */
public void rotate2(int[][] matrix) {
    int n = matrix.length;

    // 沿主对角线翻转
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n - i; j++) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[n - j - 1][n - i - 1];
            matrix[n - j - 1][n - i - 1] = temp;
        }
    }

    // 上下翻转
    for (int i = 0; i < (n >> 1); i++) {
        for (int j = 0; j < n; j++) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[n - i - 1][j];
            matrix[n - i - 1][j] = temp;
        }
    }
}

代码解读

方法一解读

  • 分层遍历:通过 i 控制当前层的起始位置,len 计算当前层的边长。
  • 元素交换:通过四个位置的坐标关系,依次交换值,完成当前层的旋转。
  • 逐层收缩:每次处理完一层后,进入下一层,直到所有层处理完毕。

方法二解读

  • 主对角线翻转:将矩阵沿主对角线对称翻转,相当于交换 (i, j)(j, i) 的值。
  • 上下翻转:将矩阵沿水平中轴翻转,相当于交换第 i 行和第 n-i-1 行的值。

性能分析

时间复杂度

  • 方法一:O(n²),其中 n 是矩阵的边长。每个元素被访问一次。
  • 方法二:O(n²),同样每个元素被访问一次。

空间复杂度

  • 方法一:O(1),只使用了常数级别的额外空间。
  • 方法二:O(1),同样只使用了常数级别的额外空间。

测试用例

以下是几个测试用例,用于验证代码的正确性:

// 测试用例1
int[][] matrix1 = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
rotate(matrix1);
System.out.println(Arrays.deepToString(matrix1)); 
// 输出: [[7, 4, 1], [8, 5, 2], [9, 6, 3]]

// 测试用例2
int[][] matrix2 = {
    {5, 1, 9, 11},
    {2, 4, 8, 10},
    {13, 3, 6, 7},
    {15, 14, 12, 16}
};
rotate(matrix2);
System.out.println(Arrays.deepToString(matrix2)); 
// 输出: [[15, 13, 2, 5], [14, 3, 4, 1], [12, 6, 8, 9], [16, 7, 10, 11]]

// 测试用例3
int[][] matrix3 = {
    {1}
};
rotate(matrix3);
System.out.println(Arrays.deepToString(matrix3)); 
// 输出: [[1]]

扩展讨论

方法对比

  • 分层旋转法:适合对矩阵的边界操作有直观理解的开发者,逻辑较为复杂,但性能优秀。
  • 数学变换法:逻辑清晰,代码简洁,适合快速实现,且易于扩展到其他类似问题。

实际应用场景

  • 图像处理:旋转图像是计算机视觉中的常见操作。
  • 数据转换:矩阵旋转可用于数据的重新排列和格式化。

总结

本题考察了对矩阵操作的理解和实现能力。通过两种不同的方法,我们展示了如何高效地解决矩阵旋转问题。无论是分层旋转法还是数学变换法,都有其独特的优点,开发者可以根据实际需求选择合适的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值