第4章串、数组和广义表
第4章串、数组和广义表
4.1串的定义
1. 串的定义
- 文字定义:由零个或多个字符组成的有限序列。
- 符号化表示:记作 S=′a1a2⋯an′(n≥0),其中 S 为串名,a1a2⋯an为串值,n 为串的长度。
关于“串的定义”的考试要点:
- 严谨性要求: 考试中回答定义时必须严谨。核心点是“由零个或多个字符组成”。老师特别强调,很多同学容易忽略“零个”的情况,只回答“若干个”,这是不完整的,“零个”字符也是一个合法的串。
- 两种表示形式(满分答法): 考试中如果能同时给出以下两种形式,通常是满分标准:
- 文字定义: “由零个或多个字符组成的有限序列”。
- 符号化表示: S=′a1a2⋯an′(n≥0)。其中n 可以等于 0,表示空串。
2. 串的术语
| 术语 | 课件定义 | 【老师补充讲解】 |
|---|---|---|
| 串名 | 串的标识(如上述 S) | 通常用大写字母(如 、)表示,用于区分不同串,无实际字符意义。 |
| 串值 | 用单引号括起来的字符序列 | 单引号是串值的 “标识符号”,不属于串的组成部分;例如 ′BE**I′ 中,串值为 “BEI”。 |
| 长度 | 串中字符的数目 | 长度最小为 0(空串),最大为有限值;需注意 “空格” 算一个字符(如 ′BEIJIN**G′ 长度为 8)。 |
| 空串 | 含零个字符的串(记为 ∅) | 空串 ≠ 空格串:空格串是由 1 个或多个 “键盘 space 键” 组成的串(如 长度为 3),属于非空串。 |
| 空格串 | 由一个或多个空格组成的串 | 空格是合法字符,需计入长度;例如课件中 d=′BEIJIN**G′ 因含 1 个空格,长度比 c=′BEIJIN**G′ 多 1。 |
| 子串 | 串中任意个连续的字符组成的子序列 | ① 关键属性:“连续”—— 非连续字符序列不属于子串(如 ′BEIJIN**G′ 中 “BJ” 不是子串);② 任意范畴:“零个或多个字符”(零个子串即空串,多个字符需连续)。 |
| 字符在串的位置 | 字符在序列中的序号 | 序号有两种计数方式:① 从 1 开始(常用,如 ′BE**I′ 中 “B” 位置为 1);② 从 0 开始(编程中常见,如 ′BE**I′ 中 “B” 位置为 0),需结合场景判断。 |
| 子串在串的位置 | 子串的第一个字符在串中的位置 | 例如 ′JIN**G′ 在 ′BEIJIN**G′ 中位置为 4(从 1 计数),因第一个字符 “J” 在原串第 4 位;注意不是子串最后一个字符的位置。 |
| 相等 | 当且仅当两个串的值相等 | 需同时满足 “长度相等”+“对应位置字符完全一致”;例如 ′ABC′\=′ABD′(第 3 位字符不同),′A**B′\=′ABC′(长度不同)。 |
关于“串的术语”详解:
- 串值 (String Value): 注意是用单引号括起来的字符序列(例如
'abc'),这是表征串值的标准方式。 - 空串 (Empty String): 长度为 0,不含任何字符。
- 空格串 (Blank String): 千万别混淆! 空格串不是空串。它是由一个或多个“空格字符”(键盘上的 Space 键)组成的串。空格也是字符,占用长度。
- 子串 (SubString) 的核心定义:
- 定义:串中任意个、连续的字符组成的子序列。
- 关键点 1 “任意个”: 这里的“任意”范围是零个或零个以上。零个字符(空串)也是任意串的子串。
- 关键点 2 “连续”: 这是判断是否为子串的最核心标准。必须是原串中连续在一起的一段字符,不能跳着取。
- 位置 (Position):
- 字符位置: 指的是序号。注意序号可能从 0 开始(计算机习惯),也可能从 1 开始(人类习惯),具体看题目约定。
- 子串位置: 指的是该子串在主串中第一个字符出现的位置(首字符索引)。
3. 串的举例
课件示例:a=′BEI′、b=′JING′、c=′BEIJING′、d=′BEIJING′
-
长度:分别为 3、4、7、8;
-
子串关系:a 和 b 均是 c 和 d 的子串;
-
位置:a 在 c 和 d 中位置均为 1;b 在 c 中位置为 4,在 d 中位置为 5(因 d 中 “BEI” 后有 1 个空格,“J” 在第 5 位);
-
相等判断:a、b、c、d 彼此不相等(长度或串值不同)。
-
空格的影响:d 因含 1 个空格,长度比 c 多 1,且 b 在 d 中的位置比在 c 中多 1,需注意空格对 “位置” 和 “长度” 的影响;
-
考试常见题型:给定多个串,判断子串关系、计算长度或位置,需重点关注空格字符。
4. 串的比较
串的比较: 通过组成串的字符之间的比较来进行的。
给定两个串:X='x1x2...xn' 和Y='y1y2...yn' ,则:
-
当n=m且 x1=y1, ..., xn=yn 时,称 X=Y;
-
当下列条件之一成立时,称 X < Y:
- n<m且 xi=yi(1 ≤ i ≤ n);
- 存在 k ≤ min(m,n),使得 xi=yi(1 ≤ i ≤ k-1)且 xk<yk。
串比较的核心规则(通俗理解):
-
对应字符比较: 两个串(串 1 和串 2)从第一个位置开始,两两比较相同位置上的字符。
-
停止时机: 一旦发现相同位置上的字符不一样,立即停止比较。
-
大小判定: 停止位置上,哪个字符的 ASCII 码大,所在的那个串就大。
-
ASCII差 = 0 →字符相等。
-
ASCII差 < 0 → 前者小。
-
ASCII差 > 0 → 前者大。
-
-
重要误区: 串的大小绝不是比长度! 并不是字符串越长就越大。例如 "A..." 很长,但只要第一个字符比对方小,整个串就小。
形式化定义详解(考研/科研基础): 老师强调要掌握这种“形式化语言”,这是将文字描述转化为公式推理的基础。
- 相等 (\(X=Y\)): 必须满足两个条件:① 长度相等 (n=m);② 对应位置字符完全一致。
- 小于 ( X < Y) 的两种情况:
- 情况 1(前缀相同,X 短):X的所有字符都和 Y 的前 n 个字符一样,但 X 比 Y短 (n<m)。
- 例子: X='ABC', Y='ABCD'。X 是 Y 的前缀,且更短,所以 X< Y 。
- 情况 2(出现字符差异): 在第k个位置首次出现不同 (xk≠_y_k),而在 k 之前的所有字符都相同。如果此时 xk<yk,则 X< Y 。
- 例子 1: X='ABC', Y='ZHYK'。第 1 个字符 'A' < 'Z',直接判定 X< Y。
- 例子 2: X='ABCDEFG...'(很长), Y='ZHY'。第 1 个字符 'A' < 'Z',依然是 X< Y,与长度无关。
- 情况 1(前缀相同,X 短):X的所有字符都和 Y 的前 n 个字符一样,但 X 比 Y短 (n<m)。
5.串与线性表的区别
| 对比维度 | 串 | 线性表 |
|---|---|---|
| 数据对象 | 仅限定为字符集(如 ASCII 字符、Unicode 字符) | 无限制(可是数字、结构体、字符等任意数据类型) |
| 基本操作对象 | 以 “串的整体” 为单位(如查找子串、插入子串、删除子串) | 以 “单个元素” 为单位(如查找单个元素、插入单个元素、删除单个元素) |
为什么把“串、数组、广义表”放在一章?
- 这一章展示了从线性到非线性,再到统一范式的演变。
- 串: 简单的线性结构。
- 数组: 一维数组是线性,二维及以上是非线性,但结构规整。
- 广义表: 是本章的升华。它可以包含异构数据(原子或子表),能将线性结构(串、数组)和非线性结构(树、图)统一到一个框架(范式)下进行表示。
考试要求:
- 串: 掌握 BF 和 KMP 算法。
- 数组: 掌握两类操作——压缩(特殊矩阵、稀疏矩阵)和表征(地址计算)。
- 广义表: 理解其递归定义和基本运算(Head/Tail)。
4.2串的类型定义、存储及其运算
知识点1: 串的表示
知识点2: 串的模式匹配
章节重心: 串的知识点分三块(定义、存储、运算),本节重心是串的运算,特别是串的模式匹配(考试必考)。
实用价值: 数据结构课程中,主要通过匹配算法来掌握串的工程应用价值。
4.2.1串的表示
串的表示:3种方法
(1)方法1:定长顺序存储表示
- 定义: 用一组地址连续的存储单元存储串值的字符序列。
- 形式:
- (1) 非压缩形式:一个数组单元存储一个字符——浪费空间。
- (2) 压缩形式:一个数组单元存储多个字符——算法复杂。
如何表示串的长度?
- 方案1:用一个变量来表示串的实际长度。
- 方案2:在串尾存储一个不会在串中出现的特殊字符作为串的终结符,表示串的结尾。
- 方案3:用数组的0号单元存放串的长度,从1号单元开始存放串值。(图示显示了下标0存放长度,1~n存放字符)

- 定长顺序存储的核心是 “地址连续”,本质是利用线性表的顺序存储特性存储字符序列,理解即可,无需深入复杂实现。
- 串长度表示的三种方案中,方案 3(0 号单元存长度,1 号单元存字符)是考试及实际应用中最常用的方式,后续模式匹配算法(BF、KMP)均默认此方案。
(2)方法2:堆分配存储表示
系统开辟一个串值存储空间(串值可利用空间) ,同时建立一个符号表;
建立一个新串时,在可利用空间分配,并在符号表中记录下串变量名、串值在可利用空间的位置、串长度等信息。

(包含 “符号表” 和 “串值存储空间” 两部分,符号表记录串的关键属性,串值存储空间存储实际字符序列)
- 堆分配存储的核心是 “动态分配”,空间可可逆利用(即无需使用时可释放回系统),解决了定长存储 “空间固定、易浪费或溢出” 的问题。
- 符号表的作用是 “索引”,通过记录串变量名与串值位置的对应关系,快速定位和管理串值,类似 “目录与文件” 的对应关系。
(3)方法3:串的块链存储表示
【课件原始内容】
-
定义: 用链表方式存储串值,每个结点大小相同。结点分为两个域:data域、next域。
-
形式:
-
结点大小为1的链表(非压缩形式):
head -> [A|next] -> [B|next] ...‘,节点大小为 1(即一个 data 域只存一个字符),结构简单,易实现,但指针域占比高,空间利用率低。
-
结点大小为4的链表(压缩形式):
head -> [ABCD|next] -> [EF##|next],一个 data 域存储多个字符(如存 4 个字符),最后一个 data 域若未填满,用特殊字符填充,可提高空间利用率。
-
-
存储密度: 存储密度 = 串值所占的存储位 / 实际分配的存储位。
指实际字符所占空间与结点总空间(data 域 + next 域)的比值,也叫 “压缩力度”,密度越高,空间利用率越高。
4.2.2串的模式匹配
(1)模式匹配定义与特点
-
模式匹配: 给定主串S="s1s2...sn"和模式 T="t1t2...tm",在S中寻找T的过程称为模式匹配。如果匹配成功,返回T在S中的位置,如果匹配失败,返回0。
- 主串与模式串的符号约定:主串通常用 S 表示,模式串(即要查找的子串)常用 T 或 P(P 为 “Pattern” 的缩写,意为 “模式”)。
-
基本假设: 假设串采用顺序存储结构,串的长度存放在数组的0号单元,串值从1号单元开始存放。
-
模式匹配问题的特点:
- (1) 算法的一次执行时间不容忽视:问题规模通常很大,常常需要在大量信息中进行匹配。
- (2) 算法改进所取得的积累效益不容忽视:模式匹配操作经常被调用,执行频率高。
-
介绍两种方法:
- 方法1:BF算法
- 方法2:KMP算法
-
匹配结果的明确规定:
- 成功:返回模式串在主串中第一次出现的第一个字符的位置(而非最后一个字符位置)。
- 失败:统一返回 0(而非 - 1,需注意与编程中的 “索引习惯” 区分,考试严格按此标准)。
-
讲课补充约定(与严老师考试要求一致):
- 存储结构:仅用 “顺序存储”(不用块链存储),字符连续存放,无后继指针。
- 串长度存储:串的长度存于 0 号单元,1 号单元开始存实际字符(如 S [0] 存主串长度 n,S [1]~S [n] 存主串字符;T [0] 存模式串长度 m,T [1]~T [m] 存模式串字符)。
-
模式匹配的重要性:是串运算中最核心、考试每年必考的内容(选择题、计算题均会涉及),CS 专业甚至有专门研究串的硕博方向,数据结构课程中需通过匹配算法掌握串的实用价值。
(2)模式匹配---BF算法 (Brute-Force)
①BF算法基本思想
- 基本思想: 从主串S的第一个字符开始和模式T的第一个字符进行比较,若相等,则继续比较两者的后续字符;否则,从主串S的第二个字符开始和模式T的第一个字符进行比较,重复上述过程,直到T中的字符全部比较完毕,则说明本趟匹配成功;或S中字符全部比较完,则说明匹配失败。核心特征是 “回溯”。

- 回溯的具体操作:当 S [i]≠T [j](某一位不匹配)时,主串指针 i 需回溯到 “当前比较起始位置的下一个位置”(即 i = 初始 i+1),模式串指针 j 回溯到 “起始位置 1”,重新开始新一轮比较。
- 通俗理解:类似 “逐字比对,错了就退一步重新比”,逻辑简单但效率较低,因未利用已比对过的字符信息。
②BF 算法执行过程(含案例)
Step1:初始化起始下标 ——i=1(主串 S 的起始比较位置,因 S [0] 存长度),j=1(模式串 T 的起始比较位置,因 T [0] 存长度)。
Step2:循环比较(终止条件:i>S [0] 或 j>T [0]):
2.1 若 S [i] == T [j]:i += 1,j += 1(继续比对下一位);
2.2 若 S [i] != T [j]:i = i - j + 2(主串回溯到 “当前趟起始位置 + 1”,如当前 i=3、j=3,回溯后 i=3-3+2=2),j=1(模式串回溯到起始);
Step3:结果判断:
- 若 j > T [0](模式串全部比对完成):匹配成功,返回 “i - T [0]”(即模式串在主串中第一次出现的起始位置,如 i=8、T [0]=3,返回 8-3=5);
- 若 i > S [0](主串比对完仍未匹配):返回 0,匹配失败。
例:主串S="ababcabcacbab",模式T="abcac"











详细步骤:
主串 S = “ababc”(S [0]=5,S [1]='a'、S [2]='b'、S [3]='a'、S [4]='b'、S [5]='c'),模式串 T = “abca”(T [0]=4,T [1]='a'、T [2]='b'、T [3]='c'、T [4]='a')。
- 第一趟(i=1,j=1):S [1]='a'==T [1]='a'(i=2,j=2)→ S [2]='b'==T [2]='b'(i=3,j=3)→ S [3]='a'≠T [3]='c' → 回溯 i=3-3+2=2,j=1;
- 第二趟(i=2,j=1):S [2]='b'≠T [1]='a' → 回溯 i=2-1+2=3,j=1;
- 第三趟(i=3,j=1):S [3]='a'==T [1]='a'(i=4,j=2)→ S [4]='b'==T [2]='b'(i=5,j=3)→ S [5]='c'==T [3]='c'(i=6,j=4)→ S [6] 不存在(i>5)→ 回溯 i=6-4+2=4,j=1;
- 第四趟(i=4,j=1):S [4]='b'≠T [1]='a' → 回溯 i=4-1+2=5,j=1;
- 第五趟(i=5,j=1):S [5]='c'≠T [1]='a' → 回溯 i=5-1+2=6,j=1;
- 第六趟(i=6>5):主串比对完,返回 0(此案例实际匹配失败,讲课案例表述中主串可能为 “ababcabca”,需以步骤逻辑为准,核心是理解回溯过程)。
(3)BF 算法性能分析
设串 S 长度为 n,串 T 长度为 m,在匹配成功的情况下,考虑两种极端情况:
情况 1:最好情况,即不成功匹配都发生在串 T 的第 1 个字符。设匹配成功发生在si处,则在i−1趟不成功的匹配中共比较了i−1次,第i 趟成功的匹配共比较了 m 次,所以总共比较了i−1+m次,所有匹配成功的可能情况共有n−m+1种;
情况 2:最坏情况,不成功匹配都发生在串 T 的最后一个字符。
例如: S = “aaaaaaaaaaabccccc”,T = "aaab"。
-
性能量化分析:
-
最好情况时间复杂度:O (n + m)。
例:主串 S=“abcdefgh”(n=8),模式串 T=“xyz”(m=3),每趟仅比较 T [1](第一个字符)就不匹配,共需比较 n - m + 1 = 8-3+1=6 次,接近 O (n),加上成功匹配的 m 次,总复杂度为 O (n + m)。
-
最坏情况时间复杂度:O (n×m)。
例:主串 S=“aaaaaab”(n=7,末尾为 'b'),模式串 T=“aaab”(m=4,末尾为 'b')。每趟需比较 m 次(前 3 个 'a' 匹配,第 4 个不匹配),共需 n - m + 1 = 7-4+1=4 趟,总比较次数 = 4×4=16 次,接近 O (n×m)。
-
-
BF 算法的核心缺陷:主串指针 i 的 “回溯” 导致重复比对。例如主串已比对到第 100 位,因模式串最后一位不匹配,需回溯到第 50 位重新比对,浪费已有的比对信息,效率低下。
(4)为什么BF算法时间性能低?
- 原因: 在每趟匹配不成功时存在大量回溯,没有利用已经部分匹配的结果。
- 思考: 如何在匹配不成功时主串 不回溯?
- 思路: 主串不回溯,模式就需要向右滑动一段距离。
- 解决方法: 模式匹配——KMP算法。
(5)如何再匹配不成功时主串不回溯?
主串不回溯,模式就需要向右滑动一段距离。
解决方法:模式匹配---KMP算法
- KMP 算法的核心目标:解决 BF 算法中 “主串回溯” 的缺陷,实现主串指针 i 不回溯,仅通过调整模式串指针 j 的位置,继续后续比对,大幅提升效率。
- KMP 算法的重要性:提出后对串模式匹配领域影响深远,是严老师大纲明确要求掌握的算法,其核心优势是 “利用已比对的部分匹配信息,让模式串尽可能向右滑动更远的距离”,避免重复比对。
- 后续学习重点:需掌握 KMP 算法的核心思想(部分匹配表)、next 函数的定义与计算、KMP 匹配过程,以及 nextval 函数(next 函数的改进)。
4.2.3模式匹配---KMP算法
(1)本节课知识定位
- 章节归属:属于第 4 章 “串、数组和广义表” 中4.2 串的类型定义、存储结构及其运算的知识点 2:串的模式匹配,是对 BF 算法的进阶改进,核心讲解KMP 算法的原理、
next[j]函数及优化方案,课件明确其为串运算的核心考点。 - 内容承接:本节课前需掌握串的 3 种存储结构(课件知识点 1)及 BF 模式匹配算法(课件知识点 2 的方法 1),KMP 算法针对 BF 算法 “主串指针回溯” 的缺陷进行优化。
(2)BF 算法(课件铺垫内容,KMP 算法的改进前提)
①基本思想
从主串S的第一个字符开始与模式串T的第一个字符逐位比较,相等则继续比对后续字符;若不等,主串指针i回溯至当前趟起始位置的下一位,模式串指针j回溯至 1,重复直至匹配成功或主串遍历完毕。
②算法性能
设主串长度为n,模式串长度为m,匹配成功时存在两种极端情况:
- 最好情况:失配均发生在模式串第 1 个字符,时间复杂度为O(n+m);
- 最坏情况:失配均发生在模式串最后一个字符,存在大量冗余回溯,时间复杂度为O(n×m)。
(3)KMP 算法(课件 P30-60,本节课核心内容)
①算法分析过程(课件 P30-31)
以主串s=abacaba、模式串p=abab为例,首次匹配在、时失配:
- BF 算法的缺陷:主串i回溯至 2,模式串j回溯至 1,重复无效比对;
- KMP 改进逻辑:利用 “部分匹配” 信息推导 —— 因p1\=p2且s2=p2,故s2\=p1;又因p1=p3且s3=p3,故s3=p1,因此无需回溯主串i,仅将模式串j调整至 2,直接从、开始下一轮比对,核心是主串指针不回溯。
②核心推导
当主串s[i]\≠p[j]时,前j−1位已完全匹配,联立以下两个等式可确定模式串滑动的新起点k:
-
p1p2…pk−1=si−k+1si−k+2…si−1
-
pj−k+1pj−k+2…pj−1=si−k+1si−k+2…si−1
联立得:
p1p2…pk−1=pj−k+1pj−k+2…pj−1,且k的取值仅与模式串p相关,与主串s无关。
③next[j]函数定义(课件 P34)
next[j]表示模式串第j个字符失配时,需重新与主串当前字符比对的模式串字符位置,数学定义为:
其物理意义为模式串前j−1位中最大相同首尾真子串的长度,值越大,模式串滑动距离越远,比对次数越少。
④next[j]计算方法(课件 P36)
课件将计算规则分为 3 种情形:
- 情形 1:j=1时,nex**t[j]=0(硬性规定);
- 情形 2:j>1时,若模式串p[1..j−1]存在首尾相同子串,取其最大长度*l*,则nex**t[j]=l+1;
- 情形 3:j>1且无首尾相同子串时,nex**t[j]=1。
例题实操
已知模式串T=abaabcac(j从 1 到 8),按规则计算得next[j]数组如下:
| j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 模式串字符 | a | b | a | a | b | c | a | c |
| nex**t[j] | 0 | 1 | 1 | 2 | 2 | 3 | 1 | 2 |
⑤KMP 算法匹配流程(课件 P50)
- 初始化:主串指针i=1,模式串指针j=1;
- 循环比对:直至主串剩余字符长度小于模式串长度或模式串匹配完毕:
- 若s[i]=p[j],则i++、j++,继续比对下一位;
- 若s[i]\=p[j],则j=nex**t[j](主串i不回溯);若j=0,则i++、j=1;
- 结果判定:若j>m(m为模式串长度),匹配成功,返回i−m;否则匹配失败,返回 0。
⑥next[j]函数的改进(nextval[j],课件 P55-58)
- 缺陷分析:当p[j]=p[nex**t[j]]时,会产生无效比对(如模式串aaaab,nex**t[4]=3,但p[4]=a=p[3],仍需重复滑动);
- 改进规则:比较p[j]与p[nex**t[j]],若不等则nextval[j]=nex**t[j];若相等则nextval[j]=nextval[nex**t[j]];
- 课件案例
模式串aaaab的next[j]与nextval[j]数组如下:
| j | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|
| 模式串字符 | a | a | a | a | b |
| next[j] | 0 | 1 | 2 | 3 | 4 |
| nextval[j] | 0 | 0 | 0 | 0 | 4 |
⑦KMP 算法时间复杂度(课件 P60)
- 求
next[j]数组的时间复杂度为O(m); - 主串匹配阶段因指针不回溯,比对次数为O(n);
- 算法总时间复杂度为O(n+m),远优于 BF 算法的最坏时间复杂度。
(4)典型练习(强化考点)
①练习 2
目标串s=aaabaaaab,模式串t=aaaab,其next[j]数组为[0,1,2,3,4],经nextval优化后为[0,0,0,0,4],优化后可直接跳过无效比对,从、开始匹配。
②2015 年联考真题(课件 P95)
字符串S=abaabaabacacaabaabcc,模式串t=abaabc,首次失配于i=j=5,根据next[5]=2,下次匹配时、,答案为 C 选项。
(5)核心重难点总结(课件提炼)
- KMP 核心:主串指针不回溯,利用模式串自身前后缀特性确定滑动距离,关键是
next[j]函数的计算; next[j]本质:模式串前j−1位的最大相同首尾真子串长度 + 1(j=1时为 0);nextval作用:解决next[j]的无效比对缺陷,进一步压缩比对次数;- 考点方向:
next[j]/nextval[j]的手工计算、KMP 匹配流程、时间复杂度分析。
4.3数组
4.3.1数组的基本定义
- 核心概念:数组是由固定类型、相同数据元素组成的阵列,常见维度为一维和二维。
- 二维数组的结构特性:二维数组(m行n列,元素范围a00到am−1,n−1)不属于线性结构。因为线性结构要求元素仅有一个前驱和一个后继,而二维数组元素在行和列两个维度上分别存在前驱和后继,不满足线性结构的定义。
在行关系中aij直接前驱是aij-1,aij直接后继是aij+1
在列关系中aij直接前驱是ai-1j,aij直接后继是ai+1j
4.3.2数组的存储方式
(1)一维数组
本身为线性结构,可直接进行顺序存储,无需额外的线性化处理。
(2)二维数组
由于二维结构无法直接适配线性存储介质,需进行线性化转换,存在两种核心存储策略,且对应不同编程语言的存储习惯:
- 按行存储(先行后列 / 以行为主)
- 存储顺序:先存完第 0 行的所有元素(a00→a01→⋯→a0,n−1),再依次存储第 1 行至第m−1行的元素,即 “自上向下、从左到右” 的顺序,最终将二维数组映射到一维线性空间。
- 对应语言:C、C++、C# 等编程语言采用此存储模式。
- 按列存储
- 存储逻辑与按行存储相反,优先按列的顺序完成元素存储。
- 应用场景:部分编程语言会采用该存储方式。
设A是一个具有m 行n列的元素的二维数组:
1.以行为主序的方式:

2.以列为主序的方式:

4.3.3数组相关核心计算(考试重点)
(1)二维数组元素存储地址计算
- 核心参数
- Loc(aij):元素aij的存储地址
- Loc(a00):数组的基地址(即首个元素的存储地址)
- s:单个元素占用的存储单元数量(如 C 语言中整型元素s=4)
- m、n:二维数组的行数和列数
- 计算逻辑
- 先计算aij之前已存放的元素总数:第i行前有i行,每行n个元素,第i行内j列前有j个元素,总计i×n+j个。
- 地址公式:Loc(aij)=Loc(a00)+(n×i+j)×s(下标从 0 开始)
说明:一般在程序设计过程中,一维数组和二维数组使用较普遍,超过二维以上的多维数组使用相对较少,对于高维数组的顺序存储方法,可以将二维的情形加以推广便能够得到。
四、矩阵的压缩存储
核心目标:用更少存储空间存储矩阵,同时保证能完整恢复原矩阵,分为特殊矩阵和稀疏矩阵两类压缩方案。
(1)特殊矩阵
指具有固定元素分布规律的矩阵,考试常考 4 类,分别为对称矩阵、上三角矩阵、下三角矩阵、带状(对角)矩阵。
- 对称矩阵
- 特性:以主对角线为轴,上下半区元素完全相同。
- 压缩逻辑:仅需存储主对角线及单侧半区元素,总存储量为n(n+1)/2(下标从 0 开始时,地址范围为0到n(n+1)/2−1)。
- 地址映射公式:
- 若i≥j(下半三角区域):k=i(i+1)/2+j
- 若i<j(上半三角区域):互换i和j后代入上述公式(如a53,代入得地址为 18)
- 上 / 下三角矩阵
- 特性:三角区域外的元素为固定常量。
- 压缩逻辑:只需存储三角区内元素和一个常量值,即可实现空间压缩。
- 带状(对角)矩阵
- 特性:非零元素集中在若干条对角线上(以五对角矩阵为例)。
- 压缩逻辑:采用 “补零对齐”,将对角线上的元素整理为二维数组(空缺处补零,保证每行n个元素),总存储空间为5×n;通过行列差值建立地址映射,核心公式为k=r1×n+j1(r1=i−j,j1=j,需结合矩阵实际情况调整)。

4.4广义表
4.4.1广义表
(1)基本定义
定义:广义表也称为列表,是线性表的一种扩展,也是数据元素的有限序列。
记作:LS= (d0, d1, d2, . . . . . .dn-1)。其中di既可以是单个元素,也可以是广义表。
说明:
(1)广义表的定义是一个递归定义,因为在描述广义表时又用到了广义表;
(2)在线性表中数据元素是单个元素,而在广义表中, 元素可以是单个元素, 称为单元素(原子),也可以是广义表,称为广义表的子表;
(3)n 是广义表长度;
- 该定义为递归定义,因为描述广义表时可再次引用广义表的概念;其中n为广义表的长度,即最外层包含的元素个数。
- 广义表的核心优势是能统一线性与非线性数据结构,其数据元素既可以是原子项(单个值),也可以是子表项(子表可为树、图等非线性结构),这种统一的表示范式在计算机领域及相关研究中具备重要价值。
(2)与线性表的区别
| 数据结构 | 数据元素类型 |
|---|---|
| 线性表 | 仅能为单个元素(原子) |
| 广义表 | 可包含原子(单个值),也可包含子表(广义表) |
4.4.2广义表典型示例解析
给出 5 个典型广义表示例,具体分析如下:
- A=()
- 表长:0,为空表,即最外层无任何元素。
- 补充:广义表的表示需带括号,空表的括号内无任何内容。
- B=(a,(b,c,d))
- 表长:2,最外层包含 2 个元素,分别为原子a和子表(b,c,d)。
- 讲解:一级逗号分隔的元素数量即为表长,该表第一个元素为原子,第二个为子表。
- C=(e)
- 表长:1,最外层仅包含 1 个原子e,注意与空表区分,空表无元素,此表有 1 个原子元素。
- D=(A,B,C,f)
- 表长:4,最外层包含 4 个元素,前 3 个为广义表(A为空表、B和C为非空广义表),第 4 个为原子f。
- 补充:广义表的元素可以混合原子和子表,且子表可以是已定义的其他广义表。
- E=(a,E)
- 特性:递归表,其第二个元素为自身,这类广义表会形成无限递归结构。
4.4.3广义表的核心运算:表头与表尾
(1)运算规则
- 若广义表不空,则可分成表头和表尾,反之,一对表头和表尾可唯一确定广义表。
- 对非空广义表:称第一个元素为L的表头,其余元素组成的表称为LS的表尾;
具体如下:
- 前提:仅非空广义表可进行表头、表尾拆分;且一对表头和表尾可唯一确定一个广义表。
- 表头(HEAD):非空广义表的第一个元素,该元素可以是原子,也可以是子表。
- 表尾(TAIL):非空广义表除去表头后,剩余元素组成的新广义表,表尾一定是广义表(需用括号包裹)。
(2)实例运算
- 对B=(a,(b,c,d))的运算
- 表头:HEAD(B)=a(第一个元素为原子a)
- 表尾:TAIL(B)=((b,c,d))(剩余元素为子表(b,c,d),需包裹成广义表)
- 对C=(e)的运算
- 表头:HEAD(C)=e
- 表尾:TAIL(C)=()(除去原子e后无剩余元素,表尾为空表)
- 嵌套运算
- HEAD(TAIL(B))=HEAD(((b,c,d)))=b
- TAIL(TAIL(B))=TAIL(((b,c,d)))=(c,d)
(3)老师讲课易错点提醒
- 表尾一定是广义表,即使剩余元素只有 1 个,也需用括号包裹,如C=(e)的表尾是()而非空值;
- 表头是第一个元素,与元素类型无关,原子和子表均可作为表头。
4.4.4广义表的存储结构
由于广义表中数据元素可以具有不同结构,故难以用顺序结构表示广义表。通常采用链表存储方式,
如何设定链表结点?广义表中的数据元素可能为单元素(原子)或子表,由此需要两种结点:一种是表结点,用以表示广义表;一种是单元素结点,用以表示单元素(原子)。

设计两种结点类型:
(1)结点结构
| 结点类型 | 结构组成 | 说明 |
|---|---|---|
| 原子结点(tag=0) | tag(标志域)+ val(值域) | tag=0 表示该结点存储原子,val 存放原子具体值 |
| 表结点(tag=1) | tag(标志域)+ child(子表指针域) | tag=1 表示该结点存储子表,child 指向子表的链式结构 |
(2)补充
- 标志位 tag 是区分结点类型的核心,0 对应原子、1 对应子表;
- 若子表中还嵌套子表,则表结点的 child 指针会继续指向更低层级的表结点,形成多层链式结构。
4.4.5学习重点总结
- 掌握广义表的递归定义,明确其与线性表的本质区别;
- 熟练判断广义表的表长,区分空表与仅含单个原子的广义表;
- 牢记表头、表尾的运算规则,尤其是 “表尾必为广义表” 的核心特性;
- 理解链式存储的结点设计逻辑,能区分原子结点与表结点的结构差异。
参考资料:教材《数据结构 C 语言 第 3 版》 数据结构考研指导(基础篇) 、数据结构考研指导(基础篇) 视频课程|赵海英

浙公网安备 33010602011771号