矩阵操作优化:从 O(q×n) 到 O(q) 的优雅进阶

最近在刷题时遇到一个很有意思的矩阵操作问题:给定一个 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

操作过程:

  1. 交换行 0 和行 1 → 矩阵变为:

    4 5 6
    1 2 3
    7 8 9

  2. 打印 (0,0) → 4

  3. 打印 (1,0) → 1

  4. 交换列 0 和列 1 → 矩阵变为:

    5 4 6
    2 1 3
    8 7 9

  5. 打印 (0,0) → 5

  6. 打印 (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)

仔细想想,我们真的需要每次都去修改矩阵吗?其实矩阵中的每个元素都可以通过公式计算出来:

matxy = (n × x) + y + 1

如果我们能记录下每次交换操作对行和列的影响,那么查询时直接根据公式计算即可!

具体做法是用两个辅助数组 rowsm 和 colsn,分别记录当前第 i 行/列对应的是原始矩阵中的哪一行/列。

  • rowsi 表示当前第 i 行对应原始矩阵的第 rowsi
  • colsj 表示当前第 j 列对应原始矩阵的第 colsj

交换行时,只需要交换 rows 数组中的两个元素;交换列时,只需要交换 cols 数组中的两个元素。

查询位置 (x, y) 时,实际元素为:
matrows\[x]cols\[y] = (rowsx × n) + colsy + 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)。这种思想在很多场景下都适用,比如处理大量交换操作的问题时,可以优先考虑是否能用映射关系来优化。

希望这篇文章对你有帮助!如果你有其他优化思路,欢迎在评论区讨论~

相关推荐
8Qi841 分钟前
LeetCode 235. 二叉搜索树的最近公共祖先(LCA)
算法·leetcode·二叉树·递归·二叉搜索树·lca·迭代
bIo7lyA8v1 小时前
算法稳定性分析中的随机扰动建模的技术8
算法
sugar__salt1 小时前
从栈队列数据结构到JS原型面向对象全解
前端·javascript·数据结构
科研online1 小时前
基于多源数据和XGBoost-SHAP分析中国大陆绿地碳汇空间变异影响因素的非线性相关性与尺度差异
算法·学习方法
Cthy_hy2 小时前
拓扑排序超详解:原理 + Kahn 贪心算法
python·算法·贪心算法
三品吉他手会点灯2 小时前
C语言学习笔记 - 43.运算符与表达式 - 运算符1 - 运算符的分类和简单介绍
c语言·笔记·学习·算法
心之伊始2 小时前
Java 后端接入大模型:从 Token、并发到推理成本的完整估算方法
java·spring boot·性能优化·大模型·llm
VkN2X2X4b2 小时前
算法复杂度的实验验证与误差分析的技术8
算法
其利天下技术3 小时前
风扇灯无刷电机自适应算法实战指南
算法·cocos2d·无刷电机自适应算法·bldc驱动自适应算法·其利无刷电机驱动算法
8Qi83 小时前
LeetCode 494:目标和(Target Sum)—— 题解 ✅
算法·leetcode·职场和发展·动态规划·01背包