Appearance
多维数组的存储
2026 大纲 三(四)多维数组的存储(压缩存储见《特殊矩阵的压缩存储》)。
一条方法:先数完整的行,再数行内的
内存是一维的,多维数组必须线性化才能放进去。而无论几维、无论行优先还是列优先,数偏移量的方法只有一条:先数跨过了多少个完整的行(列),再数本行(列)内还差几个。
下面所有公式——包括三维、
| 存储次序 | 地址公式(0-based,元素大小 | 乘的是谁 |
|---|---|---|
一维 A[0..n-1] | — | |
| 行优先(C / Java) | 🔴 列数 | |
| 列优先(Fortran / MATLAB) | 🔴 行数 | |
| 三维行优先 | 偏移 | 面 |
| 🔴 为什么行优先乘列数 | "跨一整行"跳过的是一行里的元素个数,也就是列数;列优先反之。两个公式完全对偶( |
| 🔴 1-base 怎么办 | 不要重新推公式,代入前把下标各减 1:行优先 |
| base 是题面给的 | C 语言代码题一律 0-based;出现 |
| 🔴 随机存取的准确含义 | 不是"可以任意访问",而是存取任一元素的时间都相同( |
| 数组没有插删 | 维数与各维的界一经定义就不再改变,除初始化与销毁外只有存取和修改两种操作,所以顺序存储是它最合适的存储方式 |
| 自查一招 | 最后一个元素的偏移必须等于元素总数减一,这是检验公式对错最省事的办法 |
| 元素大小 | 题面给什么用什么,只有明说"每个元素占 1 个存储单元"才是 1 |
什么时候用哪个公式:
| 题面特征 | 用哪个公式 |
|---|---|
| 明确写"按行优先" / "按列优先" | 直接用对应公式 |
给的是 C 语言数组 int A[m][n] | 行优先(C 的默认存储次序) |
| 出现 Fortran / MATLAB 字样 | 列优先 |
| 完全没说 | 按行优先处理 |
行优先(row-major):一行一行按顺序排。
text
A[0][0] A[0][1] ... A[0][n-1] A[1][0] A[1][1] ... A[1][n-1] ... A[m-1][n-1]
└──────── 第 0 行 n 个元素 ────┘└──────── 第 1 行 n 个元素 ────┘列优先(column-major):一列一列按顺序排。
text
A[0][0] A[1][0] ... A[m-1][0] A[0][1] A[1][1] ... A[m-1][1] ... A[m-1][n-1]
└──────── 第 0 列 m 个元素 ────┘└──────── 第 1 列 m 个元素 ────┘交互可视化
点击矩阵任意元素 → 一维数组中对应下标会高亮 + 公式实时显示; 切换"行优先 / 列优先"能立刻看到同一元素映射到不同位置。
两个公式为什么是对偶的
把上面那句话落到二维上,问题就化成一句:
按行优先数:跨过的完整行是第
🔴 行优先乘的是列数
,不是行数 。 因为"跨过一整行"跳过的是一行里的元素个数,那正是列数。这是本篇最容易写反的地方——而只要每次都老实按"跨过多少个完整的行"去数,就不会错。
两个公式完全对偶:把
至于 1-based:不要重新推一套公式,只在代入前把下标各减 1。转换发生在"数偏移量"这一步,公式结构一个字不变。
一维母版、两个公式的逐步推导与 1-base 形式(想把每一步都看清就展开)
一维数组是所有公式的母版。 A[0..n-1],每个元素占
这里
行优先地址公式(0-based)。问题化成一句话:
- 跨过的完整行:第
行到第 行,共 行,每行 个元素 → 个; - 本行内的偏移:第
行里排在 之前的是 ,共 个。
列优先地址公式(0-based)。把行/列的角色对调,推导逐字对应:
- 跨过的完整列:第
列到第 列,共 列,每列 个元素 → 个; - 本列内的偏移:第
列里排在 之前的有 个。
1-based 形式:转换在哪一步做。 如果题目用
转换发生在"数偏移量"这一步,而不是公式结构上:公式仍然是 "跨过
不给列数怎么办:用两个基准点反推
有一类题目不直接告诉你
设按行优先存储,已知
这是关于
一次完整演算:二维数组
第一步,用
第二步,代入
两个易错处。其一:把
其二:算到第 5 行开头就停了。
列优先的同类演算:
- 跨过 5 个完整列(第 0
4 列),每列 个元素 → 个; - 本列内偏移
个(第 5 列里 排在它前面)。
如果同一条件改成行优先:跨过 4 个完整行
自查:列优先算式里出现的是行数
,行优先算式里出现的是列数 。 如果你写出来的式子里乘的那个数和"跨过的是行还是列"对不上,一定是记混了。
反推题的列优先版本方法完全一样,只是把公式换成列优先后再相减:
这里未知的是行数
推广到三维与 n 维的映像函数(题目给三维数组时展开)
三维数组
推法还是同一句话:跨过
推广到
严蔚敏教材把它写成
"数组元素的存储位置是其下标的线性函数,一旦确定了数组各维的长度,
就是常数。 由于计算各个元素存储位置的时间相等,所以存取数组中任一元素的时间也相等, 即数组是一种随机存取结构。"
"随机存取"这四个字的准确含义就在这里:不是"可以任意访问", 而是访问任一元素的时间都相同(
三维演算:
- 跨过
个完整的"二维面",每面 个元素 → ; - 在第 2 个面内,跨过
个完整行,每行 个 → ; - 在该行内偏移
个。
偏移合计
自查:数组共
为什么"数组"被放在栈、队列这一章(想弄清大纲编排逻辑就展开)
大纲这一章叫「栈、队列和数组」。一眼看上去三者不太搭——栈和队列是操作受限的线性表, 数组明明是更基础的东西。
把它们绑在一起的真正原因:这一章讲的是"线性表怎么落到内存上"。 线性表在前一章给了抽象定义,到这一章要落地:
- 栈:限制只能在一端进出,落到内存上是数组(顺序栈)或链表(链栈);
- 队列:限制一端进、另一端出,落到内存上是数组(循环队列)或链表(链队);
- 数组:定义本身就规定了"按下标随机存取、元素按某种次序连续存储"—— 内存映射规则是整章最底层的一块拼图。
严蔚敏教材对数组的定义是:"一个
数组还有一条与线性表不同的性质:数组一旦定义,维数和各维的界就不再改变, 因此除了初始化和销毁,数组只有存取元素和修改元素值两种操作,没有插入和删除。 正因为不需要插删,顺序存储是数组最合适的存储方式。
考点速记
三条会被反复调用的结论:
- 只有一条方法:先数跨过了多少个完整的行(列),再数本行(列)内还差几个。三维、
维、压缩存储全是它的实例。 - 行优先乘列数、列优先乘行数,两个公式完全对偶,记一个就够。
- 1-base 只影响"数偏移量"这一步,代入前把下标各减 1,公式结构不变。
这一节在真题里被考过的形式(下方「真题练习」逐题对应):最典型的一种是给两个元素的地址,求第三个元素的地址——题面故意不给每行的元素个数。三步走:
- 把两个已知地址代入行优先公式相减,消掉首地址,得到关于
的一元一次方程; - 解出
; - 再代回去算目标元素。
以"每元素占 1 个单元、
易错:把地址差直接除以行数。
是错的—— 不仅跨了 3 行,列方向也跨了 3 列,方程必须写成 。
易错:算到目标行的开头就停了。
不是 ,第 5 行开头之后还要再加 5 个列偏移。
易错:乘错了那个数。 行优先乘列数、列优先乘行数;反推题里也要看清未知的到底是
还是 。
易错:元素大小
当成 1。 题面写"每个元素占 4 个存储单元"就必须乘 4,只有明说占 1 个单元才是 1。
教材出处
- 数组的定义(
维数组类型可定义为数据元素为 维数组类型的一维数组类型)、 ADT Array 的操作集(只有存取与修改,无插删)、以及"数组一旦被定义, 它的维数和维界就不再改变":严蔚敏《数据结构(C 语言版)》(第 2 版)p99「4.4.1 数组的定义」 - 4.4.2 数组的顺序存储:以行序为主序 / 以列序为主序两种存储方式(图 4.11)、
维数组的映像函数式(4-13)( , ), 以及"数组元素的存储位置是其下标的线性函数……数组是一种随机存取结构": 同书 p99–p100
相关知识
特殊矩阵的压缩存储(对称、三角、三对角、稀疏四类的下标推导,全部建立在本篇的地址公式之上)| 顺序表(一维数组最直接的应用)| 邻接矩阵(无向图的邻接矩阵是对称矩阵,理论上可以压缩)| 数组与特殊矩阵的压缩存储(拆分前的旧合并视图)