题目描述

✅ 48. 旋转图像

image-20260928195410204

image-20260928195410205

题意分析

给定 n × n 的正方形矩阵,要求把整个图像顺时针旋转 90°,直接修改原矩阵,不返回一个新矩阵,也不能另建同等大小的矩阵保存结果。

旋转改变的是元素位置,元素值本身不变。原来的第一行会成为旋转后的最后一列;如果逐格直接写入目标位置,就可能覆盖尚未搬走的旧值,因此需要安排不会丢失数据的原地交换。

解法:转置矩阵后反转每一行

核心思路

[!blue]

先明确目标坐标。使用从 0 开始的下标,原位置 (i, j) 顺时针旋转后位于 (j, n - 1 - i):原列下标变成新行下标,原行越靠上,新列就越靠右。

这个映射可以拆成两个容易原地完成的步骤。第一步沿主对角线转置,把 (i, j) 与 (j, i) 交换,元素从 (i, j) 到达 (j, i)。第二步反转每一行,把列下标 i 变成 n - 1 - i,于是元素最终到达 (j, n - 1 - i),与旋转要求完全一致。

转置时只遍历主对角线一侧,也就是 j > i 的上三角。每对对称位置只交换一次;若两侧都处理,同一对元素会被交换两次而回到原位。主对角线上的元素转置前后位置不变,无需交换。

行反转使用左右指针向中间靠拢,每次交换一对首尾元素。两步都只是交换,不会丢失尚未处理的值,也不需要额外矩阵;奇数长度行的中间元素无需交换。

解题步骤

  1. 读取矩阵边长 n。
  2. 对每一行 i,从 j = i + 1 开始遍历上三角,将 matrix[i][j] 与 matrix[j][i] 交换,完成转置。
  3. 对转置后的每一行,设置左右指针,从两端向中间交换元素,完成行反转。
  4. 两步结束后,原矩阵已经变成顺时针旋转后的结果,无需创建或返回新矩阵。

代码实现

class Solution {
    public void rotate(int[][] matrix) {
        int n = matrix.length;

        for (int i = 0; i < n; i++) {
            // 只交换对角线一侧,每对镜像位置恰好处理一次。
            for (int j = i + 1; j < n; j++) {
                int value = matrix[i][j];

                matrix[i][j] = matrix[j][i];
                matrix[j][i] = value;
            }
        }

        // 转置后再反转每行,组合为顺时针九十度旋转。
        for (int[] row : matrix) {
            reverse(row);
        }
    }

    private void reverse(int[] row) {
        int left = 0;
        int right = row.length - 1;

        while (left < right) {
            int value = row[left];

            row[left] = row[right];
            row[right] = value;
            left++;
            right--;
        }
    }
}
func rotate(matrix [][]int) {
    n := len(matrix)

    for i := 0; i < n; i++ {
        // 只交换对角线一侧,每对镜像位置恰好处理一次。
        for j := i + 1; j < n; j++ {
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
        }
    }

    // 转置后再反转每行,组合为顺时针九十度旋转。
    for i := range matrix {
        reverseInts(matrix[i])
    }
}

func reverseInts(nums []int) {
    left, right := 0, len(nums)-1

    for left < right {
        nums[left], nums[right] = nums[right], nums[left]
        left++
        right--
    }
}

复杂度分析

  • 时间复杂度:$O(n^2)$。转置与逐行反转处理的元素总数都与 $n^2$ 同阶。
  • 空间复杂度:$O(1)$。只使用临时变量和下标,直接修改原矩阵。

关键点总结

[!green]

  • 目标映射是 (i, j) → (j, n - 1 - i),用它核对变换顺序就不会混淆方向。
  • 主对角线转置负责交换行列下标,逐行反转负责翻转新列的位置。
  • 每一对元素只交换一次,保证原地操作既不覆盖旧值,也不把变换撤销。

易错点总结

[!yellow]

  • 转置时扫描完整矩阵,会把同一对位置交换两次;内层应从 i + 1 开始。
  • 转置后反转每一列,得到的是逆时针旋转,和要求的方向相反。
  • 直接写 matrix[j][n - 1 - i] = matrix[i][j],会覆盖目标位置原有且尚未移动的值。
  • 新建矩阵后仅令局部变量 matrix = rotated,不会替换调用方原矩阵的内容,也不符合原地修改要求。
  • 该转置交换依赖行列数相等,本题保证方阵,不能把相同下标循环直接套到非方阵。

相似题目

题目 难度 关联与区别
867. 转置矩阵 简单 顺时针旋转可拆成矩阵转置再逐行反转,原题只做转置。
344. 反转字符串 简单 完成矩阵转置后,每一行都直接使用左右双指针交换实现反转;该题就是这一行内反转步骤的独立练习。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/62515833
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!