矩阵(Matrix)是由 m×n 个数 \(a_{ij}\) 排成的 m 行(row)n 列(column)的数表,通常用大写字母 A、B 等表示。其中 \(a_{ij}\) 表示位于第 i 行、第 j 列的元素。
A = [aij]m×n =
在计算机程序中,矩阵通常使用二维数组(或类似结构,如 C++ 中的 `vector
矩阵加法:只有当两个矩阵 A 和 B 的行数和列数都相同时(称为同型矩阵),它们才能相加。和矩阵 C = A + B 的每个元素等于 A 和 B 对应元素的和。
(A + B)ij = Aij + Bij
矩阵数乘:用一个标量(普通的数)k 乘以矩阵 A,结果是 A 中的每个元素都乘以 k。
(k · A)ij = k · Aij
矩阵的转置(Transpose)操作是将原矩阵 A 的行与列互换得到的新矩阵,记作 AT 或 A'。如果 A 是一个 m×n 矩阵,则其转置 AT 是一个 n×m 矩阵。
(AT)ij = Aji
简单来说,原矩阵的第 i 行变成了转置矩阵的第 i 列,原矩阵的第 j 列变成了转置矩阵的第 j 行。
遍历矩阵是指按照特定的顺序访问(读取或处理)矩阵中的每一个元素。最常见的两种遍历方式是行优先遍历和列优先遍历。
行优先遍历(Row-Major Order):这是最自然的方式,如同阅读文本一样。先访问完第一行的所有元素(从左到右),然后访问第二行的所有元素,以此类推,直到最后一行。
列优先遍历(Column-Major Order):先访问完第一列的所有元素(从上到下),然后访问第二列的所有元素,以此类推,直到最后一列。某些语言(如Fortran, MATLAB, R)默认按列存储数据,这种遍历方式在这些环境中更高效。
在矩阵中查找一个特定值的元素是常见操作。根据矩阵的特性,查找策略也不同。
暴力查找(Brute-Force Search):适用于任何普通矩阵。方法很简单:逐个检查矩阵中的每个元素,看它是否等于目标值。如果找到了,返回成功(通常还带上位置信息);如果遍历完所有元素都没找到,返回失败。
时间复杂度:O(m × n)
优化查找(适用于特定有序矩阵):如果一个矩阵满足特殊条件:每一行从左到右递增,每一列从上到下递增,那么我们可以使用一种更高效的查找方法。
思路:从矩阵的 右上角(或左下角)元素开始比较。设当前元素为 \(A_{ij}\):
重复此过程,直到找到目标值或超出矩阵边界(查找失败)。
时间复杂度:O(m + n)
对于非常大的矩阵,直接按行或按列遍历可能导致缓存未命中(Cache Miss)增多,影响性能。将大矩阵逻辑上划分为若干个小的子矩阵(块),优先处理完一个块内的所有数据,可以更好地利用 CPU 缓存的局部性原理,提高计算密集型操作(如矩阵乘法)的速度。
二维前缀和:预计算一个"和矩阵" S,其中 S[i][j] 表示原矩阵左上角到 (i, j) 位置构成的子矩阵的元素之和。这样,任意子矩阵 (x1, y1) 到 (x2, y2) 的元素和可以在 O(1) 时间内通过 S[x2][y2] - S[x1-1][y2] - S[x2][y1-1] + S[x1-1][y1-1] 计算得出。
二维差分数组:用于快速处理对矩阵某个子区域进行统一增减值的操作。通过在一个"差分矩阵" D 的四个关键点进行修改(O(1) 操作),可以实现对原矩阵一个矩形区域内所有元素加 k 的效果。查询原矩阵某个元素的值时,需要计算其对应差分矩阵的前缀和(O(n*m) 恢复)。
当矩阵中绝大多数元素为零时,称为稀疏矩阵。使用标准的二维数组存储会浪费大量空间。可以采用压缩存储方法,只记录非零元素的值及其行列索引。常见的格式有:
基于这些压缩格式,矩阵运算(如乘法、查找)也需要实现特定算法以保持效率。