第4章串、数组和广义表

第4章串、数组和广义表

4.1串的定义

1. 串的定义

  • 文字定义:由零个或多个字符组成的有限序列。
  • 符号化表示:记作 S=′a1a2an′(n≥0),其中 S 为串名,a1a2an为串值,n 为串的长度。

关于“串的定义”的考试要点:

  • 严谨性要求: 考试中回答定义时必须严谨。核心点是“由零个或多个字符组成”。老师特别强调,很多同学容易忽略“零个”的情况,只回答“若干个”,这是不完整的,“零个”字符也是一个合法的串
  • 两种表示形式(满分答法): 考试中如果能同时给出以下两种形式,通常是满分标准:
    1. 文字定义: “由零个或多个字符组成的有限序列”。
    2. 符号化表示: S=′a1a2an′(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;

  • 子串关系:ab 均是 cd 的子串;

  • 位置:acd 中位置均为 1;bc 中位置为 4,在 d 中位置为 5(因 d 中 “BEI” 后有 1 个空格,“J” 在第 5 位);

  • 相等判断:abcd 彼此不相等(长度或串值不同)。

  • 空格的影响:d 因含 1 个空格,长度比 c 多 1,且 bd 中的位置比在 c 中多 1,需注意空格对 “位置” 和 “长度” 的影响;

  • 考试常见题型:给定多个串,判断子串关系、计算长度或位置,需重点关注空格字符。

4. 串的比较

串的比较: 通过组成串的字符之间的比较来进行的。

给定两个串:X='x1x2...xn' 和Y='y1y2...yn' ,则:

  • n=mx1=y1, ..., xn=yn 时,称 X=Y;

  • 当下列条件之一成立时,称 X < Y:

    • n<mxi=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 个字符一样,但 XY短 (n<m)。
      • 例子: X='ABC', Y='ABCD'XY 的前缀,且更短,所以 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,与长度无关。

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,需注意与编程中的 “索引习惯” 区分,考试严格按此标准)。
  • 讲课补充约定(与严老师考试要求一致):

    1. 存储结构:仅用 “顺序存储”(不用块链存储),字符连续存放,无后继指针。
    2. 串长度存储:串的长度存于 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')。

  1. 第一趟(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;
  2. 第二趟(i=2,j=1):S [2]='b'≠T [1]='a' → 回溯 i=2-1+2=3,j=1;
  3. 第三趟(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;
  4. 第四趟(i=4,j=1):S [4]='b'≠T [1]='a' → 回溯 i=4-1+2=5,j=1;
  5. 第五趟(i=5,j=1):S [5]='c'≠T [1]='a' → 回溯 i=5-1+2=6,j=1;
  6. 第六趟(i=6>5):主串比对完,返回 0(此案例实际匹配失败,讲课案例表述中主串可能为 “ababcabca”,需以步骤逻辑为准,核心是理解回溯过程)。

(3)BF 算法性能分析

设串 S 长度为 n,串 T 长度为 m,在匹配成功的情况下,考虑两种极端情况:

情况 1:最好情况,即不成功匹配都发生在串 T 的第 1 个字符。设匹配成功发生在si处,则在i−1趟不成功的匹配中共比较了i−1次,第i 趟成功的匹配共比较了 m 次,所以总共比较了i−1+m次,所有匹配成功的可能情况共有nm+1种;

\[\sum_{i=1}^{n-m+1} p_i (i - 1 + m) = \frac{(n+m)}{2} = O(n+m) \]

情况 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)本节课知识定位

  1. 章节归属:属于第 4 章 “串、数组和广义表” 中4.2 串的类型定义、存储结构及其运算知识点 2:串的模式匹配,是对 BF 算法的进阶改进,核心讲解KMP 算法的原理、next[j]函数及优化方案,课件明确其为串运算的核心考点。
  2. 内容承接:本节课前需掌握串的 3 种存储结构(课件知识点 1)及 BF 模式匹配算法(课件知识点 2 的方法 1),KMP 算法针对 BF 算法 “主串指针回溯” 的缺陷进行优化。

(2)BF 算法(课件铺垫内容,KMP 算法的改进前提)

①基本思想

从主串S的第一个字符开始与模式串T的第一个字符逐位比较,相等则继续比对后续字符;若不等,主串指针i回溯至当前趟起始位置的下一位,模式串指针j回溯至 1,重复直至匹配成功或主串遍历完毕。

②算法性能

设主串长度为n,模式串长度为m,匹配成功时存在两种极端情况:

  1. 最好情况:失配均发生在模式串第 1 个字符,时间复杂度为O(n+m);
  2. 最坏情况:失配均发生在模式串最后一个字符,存在大量冗余回溯,时间复杂度为O(n×m)。

(3)KMP 算法(课件 P30-60,本节课核心内容)

①算法分析过程(课件 P30-31)

以主串s=abacaba、模式串p=abab为例,首次匹配在、时失配:

  1. BF 算法的缺陷:主串i回溯至 2,模式串j回溯至 1,重复无效比对;
  2. KMP 改进逻辑:利用 “部分匹配” 信息推导 —— 因p1\=p2且s2=p2,故s2\=p1;又因p1=p3且s3=p3,故s3=p1,因此无需回溯主串i,仅将模式串j调整至 2,直接从、开始下一轮比对,核心是主串指针不回溯
②核心推导

当主串s[i]\≠p[j]时,前j−1位已完全匹配,联立以下两个等式可确定模式串滑动的新起点k

  1. p1p2…pk−1=si−k+1si−k+2…si−1

  2. 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个字符失配时,需重新与主串当前字符比对的模式串字符位置,数学定义为:

\[next[j] = \begin{cases} 0 \\ \max \{ k \mid 1 < k < j \mathbin{\color{#1e88e5}{\text{且}}} p_1 p_2 \dots p_{k-1} = p_{j-k+1} p_{j-k+2} \dots p_{j-1} \} \\ 1 \end{cases} \]

其物理意义为模式串前j−1位中最大相同首尾真子串的长度,值越大,模式串滑动距离越远,比对次数越少。

next[j]计算方法(课件 P36)

课件将计算规则分为 3 种情形:

  1. 情形 1j=1时,nex**t[j]=0(硬性规定);
  2. 情形 2j>1时,若模式串p[1..j−1]存在首尾相同子串,取其最大长度*l*,则nex**t[j]=l+1;
  3. 情形 3j>1且无首尾相同子串时,nex**t[j]=1。
例题实操

已知模式串T=abaabcacj从 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)
  1. 初始化:主串指针i=1,模式串指针j=1;
  2. 循环比对:直至主串剩余字符长度小于模式串长度或模式串匹配完毕:
    • s[i]=p[j],则i++、j++,继续比对下一位;
    • s[i]\=p[j],则j=nex**t[j](主串i不回溯);若j=0,则i++、j=1;
  3. 结果判定:若j>mm为模式串长度),匹配成功,返回im;否则匹配失败,返回 0。
next[j]函数的改进(nextval[j],课件 P55-58)
  1. 缺陷分析:当p[j]=p[nex**t[j]]时,会产生无效比对(如模式串aaaabnex**t[4]=3,但p[4]=a=p[3],仍需重复滑动);
  2. 改进规则:比较p[j]与p[nex**t[j]],若不等则nextval[j]=nex**t[j];若相等则nextval[j]=nextval[nex**t[j]];
  3. 课件案例

模式串aaaabnext[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)
  1. next[j]数组的时间复杂度为O(m);
  2. 主串匹配阶段因指针不回溯,比对次数为O(n);
  3. 算法总时间复杂度为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)核心重难点总结(课件提炼)

  1. KMP 核心:主串指针不回溯,利用模式串自身前后缀特性确定滑动距离,关键是next[j]函数的计算;
  2. next[j]本质:模式串前j−1位的最大相同首尾真子串长度 + 1(j=1时为 0);
  3. nextval作用:解决next[j]的无效比对缺陷,进一步压缩比对次数;
  4. 考点方向next[j]/nextval[j]的手工计算、KMP 匹配流程、时间复杂度分析。

4.3数组

4.3.1数组的基本定义

  1. 核心概念:数组是由固定类型、相同数据元素组成的阵列,常见维度为一维和二维。
  2. 二维数组的结构特性:二维数组(mn列,元素范围a00am−1,n−1)不属于线性结构。因为线性结构要求元素仅有一个前驱和一个后继,而二维数组元素在行和列两个维度上分别存在前驱和后继,不满足线性结构的定义。

\[\color{black}{A_{m \times n}=} \begin{pmatrix} a_{00} & a_{01} & & a_{0 \ n-1} \\ a_{10} & a_{11} & & a_{1 \ n-1} \\ \\ a_{m-1 \ 0} & a_{m-1 \ 1} & & a_{m-1 \ n-1} \end{pmatrix} \]

在行关系中aij直接前驱是aij-1aij直接后继是aij+1

在列关系中aij直接前驱是ai-1jaij直接后继是ai+1j

4.3.2数组的存储方式

(1)一维数组

本身为线性结构,可直接进行顺序存储,无需额外的线性化处理。

(2)二维数组

由于二维结构无法直接适配线性存储介质,需进行线性化转换,存在两种核心存储策略,且对应不同编程语言的存储习惯:

  1. 按行存储(先行后列 / 以行为主)
    • 存储顺序:先存完第 0 行的所有元素(a00a01→⋯→a0,n−1),再依次存储第 1 行至第m−1行的元素,即 “自上向下、从左到右” 的顺序,最终将二维数组映射到一维线性空间。
    • 对应语言:C、C++、C# 等编程语言采用此存储模式。
  2. 按列存储
    • 存储逻辑与按行存储相反,优先按列的顺序完成元素存储。
    • 应用场景:部分编程语言会采用该存储方式。

设A是一个具有m 行n列的元素的二维数组:

\[\color{black}{A_{m \times n}=} \begin{pmatrix} a_{00} & a_{01} & & a_{0 \ n-1} \\ a_{10} & a_{11} & & a_{1 \ n-1} \\ \\ a_{m-1 \ 0} & a_{m-1 \ 1} & & a_{m-1 \ n-1} \end{pmatrix} \]

1.以行为主序的方式:

2.以列为主序的方式:

4.3.3数组相关核心计算(考试重点)

(1)二维数组元素存储地址计算

  1. 核心参数
    • Loc(aij):元素aij的存储地址
    • Loc(a00):数组的基地址(即首个元素的存储地址)
    • s:单个元素占用的存储单元数量(如 C 语言中整型元素s=4)
    • mn:二维数组的行数和列数
  2. 计算逻辑
    • 先计算aij之前已存放的元素总数:第i行前有i行,每行n个元素,第i行内j列前有j个元素,总计i×n+j个。
    • 地址公式Loc(aij)=Loc(a00)+(n×i+js(下标从 0 开始)

说明:一般在程序设计过程中,一维数组和二维数组使用较普遍,超过二维以上的多维数组使用相对较少,对于高维数组的顺序存储方法,可以将二维的情形加以推广便能够得到。

四、矩阵的压缩存储

核心目标:用更少存储空间存储矩阵,同时保证能完整恢复原矩阵,分为特殊矩阵稀疏矩阵两类压缩方案。

(1)特殊矩阵

指具有固定元素分布规律的矩阵,考试常考 4 类,分别为对称矩阵、上三角矩阵、下三角矩阵、带状(对角)矩阵。

  1. 对称矩阵
    • 特性:以主对角线为轴,上下半区元素完全相同。
    • 压缩逻辑:仅需存储主对角线及单侧半区元素,总存储量为n(n+1)/2(下标从 0 开始时,地址范围为0到n(n+1)/2−1)。
    • 地址映射公式:
      • ij(下半三角区域):k=i(i+1)/2+j
      • i<j(上半三角区域):互换ij后代入上述公式(如a53,代入得地址为 18)
  2. 上 / 下三角矩阵
    • 特性:三角区域外的元素为固定常量。
    • 压缩逻辑:只需存储三角区内元素和一个常量值,即可实现空间压缩。
  3. 带状(对角)矩阵
    • 特性:非零元素集中在若干条对角线上(以五对角矩阵为例)。
    • 压缩逻辑:采用 “补零对齐”,将对角线上的元素整理为二维数组(空缺处补零,保证每行n个元素),总存储空间为5×n;通过行列差值建立地址映射,核心公式为k=rn+j1(r1=ijj1=j,需结合矩阵实际情况调整)。

4.4广义表

4.4.1广义表

(1)基本定义

定义:广义表也称为列表,是线性表的一种扩展,也是数据元素的有限序列。
记作:LS= (d0, d1, d2, . . . . . .dn-1)。其中di既可以是单个元素,也可以是广义表。

说明:

(1)广义表的定义是一个递归定义,因为在描述广义表时又用到了广义表;
(2)在线性表中数据元素是单个元素,而在广义表中, 元素可以是单个元素, 称为单元素(原子),也可以是广义表,称为广义表的子表;
(3)n 是广义表长度;

  • 该定义为递归定义,因为描述广义表时可再次引用广义表的概念;其中n为广义表的长度,即最外层包含的元素个数。
  • 广义表的核心优势是能统一线性与非线性数据结构,其数据元素既可以是原子项(单个值),也可以是子表项(子表可为树、图等非线性结构),这种统一的表示范式在计算机领域及相关研究中具备重要价值。

(2)与线性表的区别

数据结构 数据元素类型
线性表 仅能为单个元素(原子)
广义表 可包含原子(单个值),也可包含子表(广义表)

4.4.2广义表典型示例解析

给出 5 个典型广义表示例,具体分析如下:

  1. A=()
    • 表长:0,为空表,即最外层无任何元素。
    • 补充:广义表的表示需带括号,空表的括号内无任何内容。
  2. B=(a,(b,c,d))
    • 表长:2,最外层包含 2 个元素,分别为原子a和子表(b,c,d)。
    • 讲解:一级逗号分隔的元素数量即为表长,该表第一个元素为原子,第二个为子表。
  3. C=(e)
    • 表长:1,最外层仅包含 1 个原子e,注意与空表区分,空表无元素,此表有 1 个原子元素。
  4. D=(A,B,C,f)
    • 表长:4,最外层包含 4 个元素,前 3 个为广义表(A为空表、BC为非空广义表),第 4 个为原子f
    • 补充:广义表的元素可以混合原子和子表,且子表可以是已定义的其他广义表。
  5. E=(a,E)
    • 特性:递归表,其第二个元素为自身,这类广义表会形成无限递归结构。

4.4.3广义表的核心运算:表头与表尾

(1)运算规则

  • 若广义表不空,则可分成表头和表尾,反之,一对表头和表尾可唯一确定广义表。
  • 对非空广义表:称第一个元素为L的表头,其余元素组成的表称为LS的表尾;

具体如下:

  • 前提:仅非空广义表可进行表头、表尾拆分;且一对表头和表尾可唯一确定一个广义表。
  • 表头(HEAD):非空广义表的第一个元素,该元素可以是原子,也可以是子表。
  • 表尾(TAIL):非空广义表除去表头后,剩余元素组成的新广义表,表尾一定是广义表(需用括号包裹)。

(2)实例运算

  1. B=(a,(b,c,d))的运算
    • 表头:HEAD(B)=a(第一个元素为原子a
    • 表尾:TAIL(B)=((b,c,d))(剩余元素为子表(b,c,d),需包裹成广义表)
  2. C=(e)的运算
    • 表头:HEAD(C)=e
    • 表尾:TAIL(C)=()(除去原子e后无剩余元素,表尾为空表)
  3. 嵌套运算
    • 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学习重点总结

  1. 掌握广义表的递归定义,明确其与线性表的本质区别;
  2. 熟练判断广义表的表长,区分空表与仅含单个原子的广义表;
  3. 牢记表头、表尾的运算规则,尤其是 “表尾必为广义表” 的核心特性;
  4. 理解链式存储的结点设计逻辑,能区分原子结点与表结点的结构差异。

参考资料:教材《数据结构 C 语言 第 3 版》 数据结构考研指导(基础篇) 、数据结构考研指导(基础篇) 视频课程|赵海英

posted @ 2025-12-06 19:29  CodeMagicianT  阅读(6)  评论(0)    收藏  举报