数据结构学到“数组与广义表”这一章,很多人心态会从链表和二叉树里突然放松,觉得二维数组能有什么花样。结果一碰到特殊矩阵的压缩存储,光是对称矩阵那个 k = i(i-1)/2 + j 就能折腾一晚上:有人公式套错,有人搞不清下标从 0 开始还是从 1 开始,还有人做完题都说不清为什么要压成一维数组。很多教材把这一节排在数组章节的 3.5,标题只有“压缩存储”四个字,实际展开后信息量非常大——它本质上是利用矩阵内部元素的分布规律,跳过重复、恒定或大量为零的数据,把矩阵映射到一个更小的存储空间里;访问任意元素时,再通过下标映射函数把位置还原出来。
如果你正在复习考研数据结构、准备 408 统考,或者期末备考刚好卡在这一节,这篇文章会比较适合你。我会按自己复习时重新推导的顺序,把对称矩阵、上/下三角矩阵、三对角矩阵以及稀疏矩阵逐个拆开讲,公式怎么来的、题目里哪些细节容易坑人、代码怎么写,都会覆盖到。下面内容偏长,但每一段都是能直接用的干货。
1. 特殊矩阵压缩存储的整体设计思路
1.1 二维数组直接存储到底浪费在哪里
先看一个最直观的例子。一个 n 阶对称矩阵,如果直接用二维数组存,需要 n*n 个元素空间。假设 n = 100,那就是 10000 个位置。但对称矩阵满足 A[i][j] = A[j][i],整个下三角加上主对角线其实只有 n(n+1)/2 个不同的数据,算下来只有 5050 个。也就是说,直接开二维数组会白白浪费接近一半空间。如果矩阵阶数再大一点,浪费就更明显。
不只是对称矩阵,三角矩阵也类似。下三角矩阵的上三角部分全是同一个常数,无论这个常数是 0 还是其他值,都没有必要把每个位置都存一遍。三对角矩阵更极端,除了主对角线以及紧挨着它的上下两条对角线,其他位置全是 0,有效数据大约只有 3n-2 个,和 n^2 比起来差距非常大。所以这里说的“特殊”,并不是元素本身特殊,而是元素的分布有很强的规律性,这种规律性让我们有机会只保存一部分数据。
正因如此,压缩存储并不是一个玄学概念,而是非常实在的空间优化手段。你只需要理解一件事:矩阵从逻辑上看是二维的,但在内存里存储时,底层终究是一段连续的一维空间。二维数组本身的二维形态,其实是编译器帮你做了“行优先”或“列优先”的展开。压缩存储的实质,就是不用编译器默认的二维展开规则,而是自己设计一套映射规则,把每一个可能被访问的二维下标 (i, j) 对应到一维数组的某个下标 k 上。
1.2 压缩存储的核心:所有公式都在数“前面有多少个元素”
很多人一看到对称矩阵、三角矩阵、三对角矩阵各自的公式就头大,总觉得每个公式长得都不一样,背了又忘。实际上,这些公式背后只有一个统一套路:先数清在这个目标元素之前已经存放了多少个有效元素,再加一,就是该元素在一维数组中的位置。如果数组下标从 0 开始,就不要再加一,直接用前面元素个数作为下标。
比如对称矩阵存下三角,按行优先,想找 A[i][j] 的位置,第一步数前 i-1 行一共存了多少个元素,第二步数第 i 行中排在 A[i][j] 前面的元素有多少个,两者相加,就是目标元素在存储序列里的序号。等差求和公式 i(i-1)/2 就是从这里冒出来的。理解了这个套路以后,你就不会再去死记硬背公式,而是每次在草稿纸上现场推一遍,顺便用第一行第一个元素之类的边界点做验证。
从数据结构整体来看,这一节还有一个隐性作用:它是从“顺序存储结构”走向“逻辑地址计算”的桥梁。之后学树和图的存储时,你会反复用到“用数组下标表达逻辑关系”的思路,比如堆排序里父节点和孩子节点的下标关系、并查集的数组表示等。把特殊矩阵压缩存储里的下标映射练熟,对后面理解这些内容都有帮助。
1.3 特殊矩阵和稀疏矩阵到底是什么关系
需要先明确一个概念边界。有些教材把稀疏矩阵也放在“特殊矩阵”这一节里讲,因为稀疏矩阵也是矩阵压缩存储的一种典型场景;但从更严谨的定义来看,两者有本质区别。对称矩阵、三角矩阵、三对角矩阵中,非零元素(或有效元素)的位置是固定的,有明确的数学规律可循,所以可以设计统一的下标公式。稀疏矩阵则不同,它里面非零元素的个数非常少,但具体出现在哪些位置没有规律,因此很难用一个统一的 i、j 到 k 的公式来表示。
正因为稀疏矩阵没有规律,它需要换成另一套思路:只记录非零元素的位置和值,也就是三元组表或十字链表。把这个关系搞清楚以后,再看不同教材的目录就不会觉得混乱了。做题时也要先分清题目说的是“特殊矩阵”还是“稀疏矩阵”,前者考下标公式,后者考三元组操作和存储结构选择,两者方向完全不一样。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 对称矩阵压缩:一次把下标公式彻底推明白
2.1 为什么拿对称矩阵当第一个重点
对称矩阵是压缩存储里最经典的例子,也是很多后续公式的基础。它的特点是 A[i][j] 和 A[j][i] 相等,所以只需要存储下三角区域(含主对角线),就能在访问任意位置时,根据 i、j 的大小关系决定是直接取存储区里的元素,还是把下标对调后再去取。
这里要注意一个区别:对称矩阵和三角矩阵都只存一半,但对称矩阵访问上三角区域时,取的是对称位置的那个元素,因为上下三角对应的值本来就相等;三角矩阵访问那些恒为常数的区域时,返回的是一个固定常量,而不是某个对称位置的元素。这两个概念如果混淆,公式题很容易丢掉一半分。
很多教材在压缩存储这节首选按行优先存下三角区域,因为 C 语言天然就是行优先的。数起来也直观:第一行存 1 个,第二行存 2 个,第三行存 3 个……第 n 行存 n 个,总数正好是 1+2+...+n = n(n+1)/2。考试中如果没有特别说明,默认也是按行优先。
2.2 三步推出 k = i(i-1)/2 + j
假设矩阵 A 的行列下标都从 1 开始,一维数组 B 的下标也从 1 开始
