矩阵的循环轮转(按层逆时针旋转)详解及代码实现
题目描述
给定一个大小为 m×nm \times nm×n 的整数矩阵 grid
,其中 m 和 n 都是偶数;同时给定一个整数 kkk。
矩阵由若干层组成,每层是矩阵中从外围到内圈的同心环。题目要求对矩阵中的每一层分别进行 逆时针循环轮转 操作,共执行 kkk 次。具体来说,一次循环轮转操作是将该层中的每个元素向逆时针方向移动一格。
例如,最外层的元素按逆时针方向整体移动一次位置;同理,内层元素也按逆时针移动一次。
需要返回执行完所有层的 kkk 次循环轮转后的矩阵。
示例说明
示例 1
输入:
grid = [[40,10],
[30,20]]
k = 1
输出:
[[10, 20],
[40, 30]]
解释:
矩阵只有一层,元素按照逆时针方向移动一格,结果如上。
示例 2
输入:
grid = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13,14, 15, 16]]
k = 2
输出:
[[3, 4, 8, 12],
[2, 11, 10, 16],
[1, 7, 6, 15],
[5, 9, 13, 14]]
解释:
矩阵有两层,分别逆时针旋转两次,最终结果如上。
题目分析
这个题目核心是理解矩阵“层”的概念和如何对每层进行逆时针旋转。
矩阵的“层”是由矩阵外围向内圈一层一层定义的。例如,对于 4×44 \times 44×4 矩阵,
- 第一层是最外圈边界的元素;
- 第二层是内圈(去掉最外圈之后剩余部分)的外围元素。
每层的元素数量是有限的,可以看成一个一维环形数组,题目要求对这个环形数组逆时针旋转 kkk 次。
解题思路
- 提取每层元素到一维数组:
遍历每层,按逆时针顺序依次收集该层的所有元素,存储到一个列表中。 - 计算实际旋转步数:
因为旋转 kkk 次可能远大于层长度,实际旋转只需考虑 kmod层长度k \mod \text{层长度},避免无效旋转。 - 旋转数组:
将提取出来的数组进行左移(逆时针旋转)操作。 - 写回矩阵:
按照之前提取的顺序,把旋转后的数组元素写回矩阵对应层的位置。 - 重复以上操作,对所有层进行旋转。
代码实现(Python)
from typing import List
class Solution:
def rotateGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
m, n = len(grid), len(grid[0])
layers = min(m, n) // 2 # 层数
res = [row[:] for row in grid] # 复制矩阵,避免原地修改影响读取
for layer in range(layers):
elements = []
# 提取当前层元素,按逆时针顺序存入elements
# 上边 (从左到右)
for j in range(layer, n - layer):
elements.append(grid[layer][j])
# 右边 (从上到下)
for i in range(layer + 1, m - layer - 1):
elements.append(grid[i][n - layer - 1])
# 下边 (从右到左)
for j in range(n - layer - 1, layer - 1, -1):
elements.append(grid[m - layer - 1][j])
# 左边 (从下到上)
for i in range(m - layer - 2, layer, -1):
elements.append(grid[i][layer])
length = len(elements)
rotate_k = k % length # 计算有效旋转步数
# 旋转元素数组,逆时针旋转相当于左移 rotate_k 个位置
rotated = elements[rotate_k:] + elements[:rotate_k]
# 将旋转后的元素写回矩阵
idx = 0
# 上边
for j in range(layer, n - layer):
res[layer][j] = rotated[idx]
idx += 1
# 右边
for i in range(layer + 1, m - layer - 1):
res[i][n - layer - 1] = rotated[idx]
idx += 1
# 下边
for j in range(n - layer - 1, layer - 1, -1):
res[m - layer - 1][j] = rotated[idx]
idx += 1
# 左边
for i in range(m - layer - 2, layer, -1):
res[i][layer] = rotated[idx]
idx += 1
return res
复杂度分析
- 时间复杂度:
每个元素被访问并移动一次,整体是O(m×n)O(m \times n )O(m×n)。 - 空间复杂度:
额外空间主要是用于存储每层元素的临时数组,最坏情况下约为矩阵元素数量的一半,空间复杂度为 O(m×n)O(m \times n)O(m×n)。
代码解析
layers = min(m, n) // 2
:计算有多少层,取矩阵较小边长的一半(偶数保证层数正确)。- 使用双重循环,提取每层元素。
- 对提取的一维数组进行旋转,避免旋转次数过大导致性能问题。
- 旋转后的数组按原路径写回结果矩阵。
方案对比
- 直接模拟旋转:
对每层进行 kkk 次单步旋转,效率极低,尤其是 kkk 大时不可行。 - 数组旋转优化:
利用取模和数组切片实现旋转,效率高且易实现。 - 空间优化:
也可以尝试在原矩阵上就地旋转,但实现复杂度增加,不易维护。
总结
本题通过将二维矩阵按层分解为多个一维数组,再对数组进行旋转,巧妙简化了矩阵旋转的复杂度。代码结构清晰,易读易维护,且能应对较大 k 的情况。
掌握此类“矩阵层遍历 + 一维旋转”技巧,对于解决类似环形数组操作及矩阵操作类问题非常有帮助。