最近在刷题时遇到一个很有意思的矩阵操作问题:给定一个 m×n 的矩阵,初始按行优先顺序填充 1 到 m×n,然后有 q 个查询,每个查询可能是交换两行、交换两列,或者打印某个位置的元素。
乍一看这题很简单,直接模拟就行,但仔细想想,如果矩阵很大、查询很多,暴力模拟的效率其实并不高。今天就来聊聊两种解法,看看怎么从 O(q×n) 优化到 O(q)。
问题描述
给定两个整数 m 和 n,表示矩阵的维度。矩阵初始按行优先顺序填充 1 到 m×n。同时给定一个 2D 数组 queries,每个查询格式为 [t, x, y]:
- t=1:交换第 x 行和第 y 行
- t=2:交换第 x 列和第 y 列
- t=3:打印位置 (x, y) 的元素
示例:
输入:m=3, n=3
queries = [[1, 0, 1], [3, 0, 0], [3, 1, 0], [2, 0, 1], [3, 0, 0], [3, 1, 0]]
输出:[4, 1, 5, 2]
初始矩阵:
1 2 3
4 5 6
7 8 9
操作过程:
-
交换行 0 和行 1 → 矩阵变为:
4 5 6
1 2 3
7 8 9 -
打印 (0,0) → 4
-
打印 (1,0) → 1
-
交换列 0 和列 1 → 矩阵变为:
5 4 6
2 1 3
8 7 9 -
打印 (0,0) → 5
-
打印 (1,0) → 2
搞懂矩阵操作从 O(q×n) 优化到 O(q) 的优雅思路,光看公式推导容易绕进去。不如试试 图码 这个宝藏网站,它用交互式动画把60多种算法和数据结构讲得一清二楚,比如矩阵链乘、动态规划这些难点,都能拖拽输入自定义数据或上传C/C++/Java/Python代码自动生成可视化过程。特别适合备战 408考研 和高校 数据结构期末考试 ,每个知识点都配了全书级的详解和可运行代码。最近复习遇到瓶颈,直接去 图码 看动画,真的一下就通了。
图码-数据结构与算法交互式可视化平台
访问网站:https://totuma.cn
解法一:暴力模拟(Naive Approach)
最直接的想法就是真的去创建一个矩阵,然后每次交换操作都去遍历整行或整列进行元素交换。
核心代码:
python
def swapRows(mat, r1, r2):
for i in range(len(mat[0])):
mat[r1][i], mat[r2][i] = mat[r2][i], mat[r1][i]
def swapCols(mat, c1, c2):
for i in range(len(mat)):
mat[i][c1], mat[i][c2] = mat[i][c2], mat[i][c1]
def solveQueries(m, n, query):
mat = [[(i * n) + j + 1 for j in range(n)] for i in range(m)]
for q in query:
if q[0] == 1:
swapRows(mat, q[1], q[2])
elif q[0] == 2:
swapCols(mat, q[1], q[2])
else:
print(mat[q[1]][q[2]], end=" ")
复杂度分析:
- 时间复杂度:O(q × n)(每次交换行/列需要遍历 n 个元素)
- 空间复杂度:O(m × n)
当 m 和 n 很大时,这个复杂度显然不够理想。
解法二:辅助数组法(Expected Approach)
仔细想想,我们真的需要每次都去修改矩阵吗?其实矩阵中的每个元素都可以通过公式计算出来:
mat[x][y] = (n × x) + y + 1
如果我们能记录下每次交换操作对行和列的影响,那么查询时直接根据公式计算即可!
具体做法是用两个辅助数组 rows[m] 和 cols[n],分别记录当前第 i 行/列对应的是原始矩阵中的哪一行/列。
- rows[i] 表示当前第 i 行对应原始矩阵的第 rows[i] 行
- cols[j] 表示当前第 j 列对应原始矩阵的第 cols[j] 列
交换行时,只需要交换 rows 数组中的两个元素;交换列时,只需要交换 cols 数组中的两个元素。
查询位置 (x, y) 时,实际元素为:
mat[rows[x]][cols[y]] = (rows[x] × n) + cols[y] + 1
核心代码:
python
def solveQueries(m, n, query):
rows = [i for i in range(m)]
cols = [i for i in range(n)]
for q in query:
if q[0] == 1:
rows[q[1]], rows[q[2]] = rows[q[2]], rows[q[1]]
elif q[0] == 2:
cols[q[1]], cols[q[2]] = cols[q[2]], cols[q[1]]
else:
print((rows[q[1]] * n) + cols[q[2]] + 1, end=" ")
复杂度分析:
- 时间复杂度:O(q)(每次操作都是 O(1))
- 空间复杂度:O(m + n)
总结
| 方法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 暴力模拟 | O(q × n) | O(m × n) |
| 辅助数组 | O(q) | O(m + n) |
这个优化思路其实很巧妙:通过记录映射关系来避免实际的数据移动,从而将每次操作从 O(n) 降到 O(1)。这种思想在很多场景下都适用,比如处理大量交换操作的问题时,可以优先考虑是否能用映射关系来优化。
希望这篇文章对你有帮助!如果你有其他优化思路,欢迎在评论区讨论~