LeetCode 每日一题笔记
0. 前言
- 日期:2025.03.21
- 题目:3643.垂直翻转子矩阵
- 难度:简单
- 标签:数组 双指针 矩阵 原地修改
1. 题目理解
问题描述 :
给你一个 m x n 的整数矩阵 grid,以及三个整数 x、y 和 k。整数 x 和 y 表示一个正方形子矩阵的左上角下标,整数 k 表示该正方形子矩阵的边长。你的任务是垂直翻转子矩阵的行顺序(即上下翻转该正方形子矩阵),返回更新后的矩阵。
示例:
输入: grid = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]], x = 1, y = 0, k = 3
输出: [[1,2,3,4],[13,14,15,8],[9,10,11,12],[5,6,7,16]]
解释:
目标正方形子矩阵的左上角为 (1,0),边长 3,覆盖行 1-3、列 0-2:
翻转前子矩阵行内容:[5,6,7]、[9,10,11]、[13,14,15]
垂直翻转后行顺序变为:[13,14,15]、[9,10,11]、[5,6,7]
列 3 不在子矩阵范围内,保持原数值,最终得到输出矩阵。
2. 解题思路
核心观察
- 垂直翻转正方形子矩阵的本质是上下交换子矩阵的行:第1行与最后1行交换、第2行与倒数第2行交换,直到中间行;
- 子矩阵的行范围是
[x, x+k-1],列范围是[y, y+k-1]; - 可通过双指针法实现原地翻转,无需额外空间:用 top 指向子矩阵顶部行,bottom 指向子矩阵底部行,交换两行对应列的元素后,top 右移、bottom 左移,直到 top ≥ bottom。
算法步骤
- 确定子矩阵的行边界:top = x(子矩阵顶部行),bottom = x + k - 1(子矩阵底部行);
- 双指针交换行元素 :
- 当 top < bottom 时,遍历子矩阵的列范围
[y, y+k-1]; - 交换 grid[top][i] 和 grid[bottom][i] 的值;
- 当 top < bottom 时,遍历子矩阵的列范围
- 更新指针:top 加 1,bottom 减 1,重复步骤 2 直到指针相遇;
- 返回结果:原地修改矩阵后直接返回。
3. 代码实现
java
package com.sheeta1998.lec.lc3643;
public class Solution {
public int[][] reverseSubmatrix(int[][] grid, int x, int y, int k) {
int top = x;
int bottom = x + k - 1;
while (top < bottom) {
for (int i = y; i < y + k; i++) {
int temp = grid[top][i];
grid[top][i] = grid[bottom][i];
grid[bottom][i] = temp;
}
top++;
bottom--;
}
return grid;
}
}
4. 代码优化说明
优化点1:参数合法性校验(增强鲁棒性)
实际场景中可添加参数校验,避免越界异常(题目保证输入合法,可选):
java
public int[][] reverseSubmatrix(int[][] grid, int x, int y, int k) {
// 校验参数合法性
if (grid == null || grid.length == 0 || grid[0].length == 0) return grid;
int m = grid.length, n = grid[0].length;
if (x + k > m || y + k > n || k <= 0) return grid;
int top = x;
int bottom = x + k - 1;
while (top < bottom) {
for (int i = y; i < y + k; i++) {
int temp = grid[top][i];
grid[top][i] = grid[bottom][i];
grid[bottom][i] = temp;
}
top++;
bottom--;
}
return grid;
}
优化点2:提取交换逻辑为独立方法(代码复用)
若需多次交换行元素,可将交换逻辑封装为方法:
java
private void swapRows(int[][] grid, int row1, int row2, int colStart, int colEnd) {
for (int i = colStart; i < colEnd; i++) {
int temp = grid[row1][i];
grid[row1][i] = grid[row2][i];
grid[row2][i] = temp;
}
}
public int[][] reverseSubmatrix(int[][] grid, int x, int y, int k) {
int top = x;
int bottom = x + k - 1;
while (top < bottom) {
swapRows(grid, top, bottom, y, y + k);
top++;
bottom--;
}
return grid;
}
5. 复杂度分析
-
时间复杂度:(O(k^2))
- 双指针遍历的行数为 (k/2)(top 从 x 到 x+k/2);
- 每行需要交换 k 个列元素;
- 总操作次数为 (k/2 \times k = O(k^2)),与原矩阵整体大小无关。
-
空间复杂度:(O(1))
- 仅使用 temp、top、bottom 等常量级临时变量;
- 所有操作均为原地修改矩阵,无额外数组/集合开销。
6. 总结
- 核心思路是双指针原地交换:利用 top/bottom 指针交换子矩阵的上下行,实现垂直翻转,无需额外空间;
- 关键技巧:明确子矩阵的行/列边界(xx+k-1、yy+k-1),仅处理该范围内的元素;
- 该方法时间和空间复杂度均最优,适合处理任意大小的合法输入矩阵。
关键点回顾
- 垂直翻转子矩阵的核心是行交换,双指针法是实现原地翻转的最优方式;
- 时间复杂度仅与子矩阵大小 (k^2) 相关,与原矩阵整体规模无关;
- 可通过参数校验增强代码鲁棒性,通过封装方法提升代码复用性。