Skip to content

数组与特殊矩阵的压缩存储

为什么要压缩存储

一个 n×n 的矩阵直接用二维数组存,空间是 n²。但如果矩阵有大量重复元素或零元素——比如对称矩阵有接近一半的元素是冗余的,稀疏矩阵 90% 以上都是 0——这些空间就白白浪费了。压缩存储的思路很直接:只存有用的那部分,用一个一维数组搞定,再通过映射公式把原来的二维下标 (i, j) 转换成一维下标 k。408 对这块的考法也很直接:给你矩阵类型和下标,算出 k 或者反过来算 i、j。公式不复杂,但下标从 0 还是从 1 开始会直接影响结果,这是最大的坑。

核心思想

  • 数组的本质:数组是线性表的推广,n 维数组可以看成"元素为 n-1 维数组"的线性表
  • 行优先 vs 列优先:二维数组在内存中是一维连续存放的,区别在于"先存完一行再存下一行"还是"先存完一列再存下一列"
  • 压缩存储的核心:利用矩阵的结构特点(对称、三角、带状等),把 n² 个元素压缩到更小的一维数组中,通过映射公式实现下标转换
  • 稀疏矩阵:非零元素极少时,用三元组表 (行号, 列号, 值) 只存非零元素

操作详解

一、数组的存储——行优先与列优先

设二维数组 a[m][n],每个元素占 L 个存储单元,起始地址为 LOC(a[0][0])

下标从 0 开始a[0..m-1][0..n-1]):

存储方式地址计算公式
行优先LOC(a[i][j]) = LOC(a[0][0]) + (i × n + j) × L
列优先LOC(a[i][j]) = LOC(a[0][0]) + (j × m + i) × L

行优先的直觉:前面有 i 整行(每行 n 个元素),再加上当前行的前 j 个元素。列优先同理,把"行"换成"列"。

下标从 1 开始a[1..m][1..n]):

存储方式地址计算公式
行优先LOC(a[i][j]) = LOC(a[1][1]) + ((i-1) × n + (j-1)) × L
列优先LOC(a[i][j]) = LOC(a[1][1]) + ((j-1) × m + (i-1)) × L

⚠️ 易错:行优先公式中乘的是列数 n(每行有 n 个元素),列优先公式中乘的是行数 m(每列有 m 个元素)。不少同学会搞反。记忆方法:行优先"跨行",跨一行跳过 n 个元素;列优先"跨列",跨一列跳过 m 个元素。

二、对称矩阵的压缩存储

对称矩阵满足 a[i][j] = a[j][i],上三角和下三角的元素完全相同,只需存其中一半。

策略:只存下三角区域(含对角线),压缩到一维数组 B[0..n(n+1)/2-1]

一维数组大小:n(n+1)/2

下标从 0 开始a[0..n-1][0..n-1]B[0..n(n+1)/2-1]):

对于元素 a[i][j],映射到 B[k]

  • i ≥ j(下三角区域):k = i(i+1)/2 + j
  • i < j(上三角区域):利用对称性 a[i][j] = a[j][i]k = j(j+1)/2 + i

下标从 1 开始a[1..n][1..n]B[0..n(n+1)/2-1]):

  • i ≥ jk = i(i-1)/2 + j - 1
  • i < jk = j(j-1)/2 + i - 1

手算示例:5×5 对称矩阵(下标从 0 开始),求 a[3][1] 在压缩数组中的位置。

i = 3, j = 1, 满足 i ≥ j,用下三角公式:
k = i(i+1)/2 + j = 3×4/2 + 1 = 6 + 1 = 7

即 a[3][1] 存储在 B[7]。

验证:下三角元素按行存放顺序为 a[0][0], a[1][0], a[1][1], a[2][0], a[2][1], a[2][2], a[3][0], a[3][1], ...,数一下 a[3][1] 确实排在第 8 个位置(下标 7)。

三、三角矩阵的压缩存储

三角矩阵的一个三角区域全是同一个常量 c(通常是 0),只需存另一个三角区域加上这个常量。

一维数组大小:n(n+1)/2 + 1(最后一个位置存常量 c)

下三角矩阵

下三角区域和对角线上有值,上三角全是常量 c。

下标从 0 开始

  • i ≥ jk = i(i+1)/2 + j(和对称矩阵一样)
  • i < jk = n(n+1)/2(指向存放常量 c 的最后一个位置)

上三角矩阵

上三角区域和对角线上有值,下三角全是常量 c。

下标从 0 开始

上三角按行存放,第 i 行有 n - i 个元素(从 a[i][i]a[i][n-1])。

  • i ≤ jk = (2n - i + 1) × i / 2 + (j - i)
  • i > jk = n(n+1)/2(指向常量 c)

公式拆解:前 i 行一共有 n + (n-1) + ... + (n-i+1) = (2n-i+1)×i/2 个元素,当前行内偏移 j - i

下标从 1 开始

  • 上三角(i ≤ j):k = (2n - i + 2) × (i - 1) / 2 + (j - i)
  • 下三角(i > j):k = n(n+1)/2

四、三对角矩阵(带状矩阵)

三对角矩阵的非零元素集中在主对角线及其相邻的两条对角线上,即 |i - j| ≤ 1a[i][j] 才可能非零。

┌ a00 a01  0   0   0  ┐
│ a10 a11 a12  0   0  │
│  0  a21 a22 a23  0  │
│  0   0  a32 a33 a34 │
└  0   0   0  a43 a44 ┘

一维数组大小:3n - 2(第一行和最后一行各 2 个元素,中间每行 3 个)

下标从 0 开始a[0..n-1][0..n-1]B[0..3n-3]):

  • 映射:k = 2i + j
  • 逆映射(已知 k 求 i, j):i = ⌊(k + 1) / 3⌋j = k - 2i

下标从 1 开始a[1..n][1..n]B[0..3n-3]):

  • 映射:k = 2i + j - 3
  • 逆映射:i = ⌊(k + 2) / 3⌋j = k - 2i + 3

手算示例:4×4 三对角矩阵(下标从 0 开始),设 LOC(B[0]) = 100,每个元素占 4 字节,求 a[2][3] 的存储地址。

第一步:求 a[2][3] 在一维数组中的下标
k = 2i + j = 2×2 + 3 = 7

第二步:计算地址
LOC(a[2][3]) = LOC(B[0]) + k × L = 100 + 7 × 4 = 128

验证:按行列出非零元素顺序为 a[0][0], a[0][1], a[1][0], a[1][1], a[1][2], a[2][1], a[2][2], a[2][3], ...a[2][3] 排在第 8 个位置(下标 7),地址 = 100 + 28 = 128。

⚠️ 易错:三对角矩阵逆映射公式 i = ⌊(k+1)/3⌋下标从 0 开始的版本。从 1 开始时变成 i = ⌊(k+2)/3⌋。考试如果只记了一个版本,代入验证一下就能避免出错。

五、稀疏矩阵

当矩阵中非零元素的个数远少于总元素个数时(通常非零元素不超过 5%),用常规二维数组存储会浪费大量空间。

三元组表:只存储非零元素,每个非零元素用一个三元组 (i, j, v) 表示(行号、列号、值)。

例如 4×5 矩阵中有 3 个非零元素:

行号 i列号 j值 v
025
148
313

三元组表通常按行优先顺序存放。其他表示方式还有十字链表(适合矩阵运算频繁的场景),408 大纲了解即可。

⚠️ 易错:对称矩阵存的是下三角还是上三角直接决定映射公式。题目说"按行优先压缩存储下三角"和"按行优先压缩存储上三角"对应的公式完全不同。审题时先确认存的是哪个三角。

公式汇总表

矩阵类型存储大小映射公式(下标从 0 开始)
对称矩阵(存下三角)n(n+1)/2i≥j: k = i(i+1)/2 + j;i<j: k = j(j+1)/2 + i
下三角矩阵n(n+1)/2 + 1i≥j: k = i(i+1)/2 + j;i<j: k = n(n+1)/2
上三角矩阵n(n+1)/2 + 1i≤j: k = (2n-i+1)×i/2 + (j-i);i>j: k = n(n+1)/2
三对角矩阵3n - 2k = 2i + j

考研高频考点

  • ⭐ 对称矩阵的压缩下标映射公式——选择题/填空题几乎每年都考,给定 (i, j) 求 k 或给定 k 反推 (i, j)
  • ⭐ 行优先/列优先的地址计算——填空题高频,直接套公式但注意行数列数别搞反
  • ⭐ 三对角矩阵的映射与逆映射——填空题常考,逆映射的取整方向容易出错
  • ⭐ 下标从 0 开始 vs 从 1 开始的公式差异——做题第一步先看清题目的下标约定,选错公式满盘皆输
  • ⭐ 上三角矩阵与下三角矩阵的公式区别——不能混用,存储的三角区域不同公式就不同
  • 稀疏矩阵的三元组表表示——概念题为主,偶尔考三元组表的存储空间计算

相关知识

  • 顺序表是数组最直接的应用——一维数组实现的线性表
  • 邻接矩阵本质上就是图的二维数组存储,无向图的邻接矩阵恰好是对称矩阵,理论上也可以压缩存储
  • 散列表的地址映射思想与矩阵压缩存储的下标映射有相似之处——都是把逻辑位置映射到物理位置