📘 題目敘述
給你一個 m x n 的整數矩陣 grid,以及三個整數 x、y、k。
其中:
x和y表示某個正方形子矩陣左上角的列與行 indexk表示這個正方形子矩陣的邊長
你的任務是把這個子矩陣做垂直翻轉,也就是把它的列順序上下顛倒。
請回傳更新後的矩陣。
條件限制
m == grid.lengthn == grid[i].length1 <= m, n <= 501 <= grid[i][j] <= 1000 <= x < m0 <= y < n1 <= k <= min(m - x, n - y)
🧠 解題思路
這題的重點是:
- 只翻轉左上角在
(x, y)、大小為k x k的那塊正方形子矩陣 - 而且翻轉方式是垂直翻轉
- 也就是:第 1 列和最後 1 列交換、第 2 列和倒數第 2 列交換……
我這份寫法是先複製一份原本的矩陣到 ngrid,然後再把答案直接寫回 grid。
這樣做的原因是:
如果直接在原本的 grid 上一邊改、一邊讀,可能前面改過的值會影響到後面還沒處理的格子。
所以我先保留一份原本狀態,之後所有要寫進去的新值,都從 ngrid 去讀。
對子矩陣中的每個位置 (x + i, y + j),它翻轉後應該對應到的原本位置是:
- 列:
(x + k - 1) - i - 行:
y + j
也就是:
- 上下列順序反過來
- 但每一列中的左右位置不變
所以直接做:
grid[x + i][y + j] = ngrid[(x + k - 1) - i][y + j]
就可以完成這個垂直翻轉。
也就是說,這題的核心就是:
只在指定的
k x k區塊內操作,並把每個位置的新值改成原本對稱列的位置值。
所有變數
ngrid:先複製出來的原矩陣,用來讀取翻轉前的值
🪜 主要流程步驟
1. 先複製一份原本的矩陣
vector<vector<int>> ngrid = grid;
這一步很重要。
因為後面要把翻轉後的結果直接寫回 grid,
如果不先複製一份,那前面改過的值可能會影響到後面還要參考的原始內容。
所以這裡先用 ngrid 保存翻轉前的矩陣狀態。
2. 只枚舉指定的 k x k 子矩陣範圍
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
這裡的 (i, j) 不是整個矩陣的座標,而是:
- 相對於子矩陣左上角
(x, y)的偏移量
所以:
x + i是目前要改的實際列位置y + j是目前要改的實際行位置
這樣就只會處理題目指定的那塊正方形區域。
3. 把目前位置改成垂直翻轉後對應列的值
grid[x + i][y + j] = ngrid[(x + k - 1) - i][y + j];
這一行是整題最核心的地方。
原本子矩陣的列範圍是:
xx + 1- ...
x + k - 1
做垂直翻轉後:
- 第
x列要拿原本第x + k - 1列的值 - 第
x + 1列要拿原本第x + k - 2列的值 - ...
- 第
x + i列要拿原本第(x + k - 1) - i列的值
而行不會改變,所以欄位仍然是:
y + j
因此這個對應公式剛好就是垂直翻轉。
4. 為什麼這樣是垂直翻轉,不是旋轉或水平翻轉
這份程式做的事情是:
- 行號反過來
- 但列中的欄位置不變
也就是:
- 上下顛倒
- 左右不動
所以它是垂直翻轉。
如果是水平翻轉,應該會改的是欄號。
如果是旋轉,列號和欄號都會一起重新對應。
但這份程式只有列被反轉,所以完全符合題意。
5. 最後回傳更新後的 grid
return grid;
當指定子矩陣內的所有位置都改完後,grid 就已經是翻轉完成的矩陣,直接回傳即可。
⏱️ 複雜度
設子矩陣邊長為 k。
- 時間複雜度:
O(k^2)
只會掃過指定的k x k子矩陣一次。 - 空間複雜度:
O(mn)
額外複製了一份整個矩陣ngrid。
💻 程式碼實作
class Solution {
public:
vector<vector<int>> reverseSubmatrix(vector<vector<int>>& grid, int x, int y, int k) {
vector<vector<int>> ngrid = grid;
for (int i = 0; i < k; i++) {
for (int j = 0; j < k; j++) {
grid[x + i][y + j] = ngrid[(x + k - 1) - i][y + j];
}
}
return grid;
}
};
🔗 題目連結
https://leetcode.com/problems/flip-square-submatrix-vertically/