当前位置: 首页 > news >正文

【软考向】Chapter 3 数据结构

  • 线性结构
    • 线性表
      • 顺序存储 —— 访问易,增删难
      • 链式存储 —— 访问难、增删易
    • 栈 —— 后进先出 和 队列 —— 先进先出
    • 字符串 —— KMP 匹配算法
  • 数组、矩阵和广义表
    • 数组
  • 树 —— 树根为第一层,最大层数为树高/深度
    • 线索二叉树
    • 哈夫曼编码
    • 树和森林 —— 树的双亲表示和孩子表示
    • 存储 —— 邻接矩阵、邻接表
    • 遍历 —— DFS、BFS
    • 最小生成树 —— Prim 算法和 Kruskal 算法
    • 拓扑排序和关键路径
    • 最短路径
      • 单源点最短路径 —— Dijkstra 算法
      • 每对顶点间的最短路径 —— Floyd 算法
  • 查找
    • 静态查找 —— 顺序查找、二分查找、分块查找
    • 动态查找 —— 二叉排序树、平衡二叉树、B 树
    • 哈希表
  • 排序

线性结构

线性表的存储结构分为顺序存储和链式存储。

线性表

顺序存储 —— 访问易,增删难

L O C ( a 1 ) LOC(a_1) LOC(a1) 表示线性表中第一个元素的存储位置,在 顺序存储结构 中,第 i i i 个元素 a i a_i ai 的存储位置为
L O C ( a i ) = L O C ( a 1 ) + ( i − 1 ) × L LOC(a_i)=LOC(a_1)+(i-1) \times L LOC(ai)=LOC(a1)+(i1)×L

其中, L L L 是表中每个数据元素所占空间的字节数。根据该计算关系,可随机存取表中的任一个元素。

在这里插入图片描述
线性表采用顺序存储结构的 优点是可以随机存取表中的元素缺点是插入和删除操作需要移动元素。在插入前要移动元素以挪出空的存储单元,然后再插入元素;删除时同样需要移动元素,以填充被删除的元素空出来的存储单元。

在表长为 n n n 的线性表中插入新元素时,共有 n + 1 n+1 n+1 个插入位置,在位置 1 (元素所在位置)插入新元素,表中原有的 n n n 个元素都需要移动。

链式存储 —— 访问难、增删易

线性表的链式存储是用通过指针链接起来的结点来存储数据元素。数据域用于存储数据元素的值,指针域则存储当前元素的直接前驱或直接后继的位置信息,指针域中的信息称为指针(或链)。

在这里插入图片描述

在这里插入图片描述
在链式存储结构中,只需要 一个指针(称为头指针,如图3-2中的 head)指向第一个结点,就可以 顺序地访问到表中的任意一个元素

在链式存储结构下进行 插入和删除,其实质都是 对相关指针的修改

在这里插入图片描述
当线性表采用链表作为存储结构时,不能对数据元素进行随机访问,但是具有 插入和删除操作不需要移动元素 的优点。

栈 —— 后进先出 和 队列 —— 先进先出

字符串 —— KMP 匹配算法

主串为 abacbcabababbcbc,模式串为 abababb,且模式串的 next 函数值如下表所示,则 KMP 算法的匹配过程如图所示。其中,i 是主串字符的下标,j 是模式串字符的下标。

在这里插入图片描述

  • 第一趟匹配从 S[0]P[0] 开始。由于 S[0] = P[0]S[1] = P[1]S[2] = P[2],所以接下来比较 S[3]P[3]由于 S[3]P[3] 不相等,所以第一趟结束
  • 要进行第二趟匹配,j = next[3] (即 j = 1第二趟从 S[3]P[1] 开始比较,仍不相等,因此第二趟结束
  • 要进行第三趟匹配,所以令 j = next[1] (即 j = 0)。第三趟从 S[3]P[0] 开始比较,由于 S[3]P[0] 不相等,所以令 j = next[0] (即 -1),此时满足条件 j == -1,显然不能令 S[3]P[-1] 进行比较,说明主串中从 i=3 开始的子串不可能与模式串相同,因此需要将 i 的值递增后再继续进行匹配过程,即令 i++j++
  • 下一趟从 S[4]P[0] 开始比较,继续匹配过程。直到某一趟匹配成功,或者由于在主串中找不到模式串而以失败结束。

数组、矩阵和广义表

数组与广义表可看作是线性表的推广,其特点是数据元素仍然是一个表。

数组

在这里插入图片描述
A A A 可看成一个行向量形式的线性表:
在这里插入图片描述
或列向量形式的线性表:
在这里插入图片描述
二维数组的存储结构可分为 以行为主序以列为主序 的两种方法,

在这里插入图片描述
设每个数据元素占用 L L L 个单元, m m m n n n 为数组的行数和列数, L o c ( a 11 ) Loc(a_{11}) Loc(a11) 表示元素 a 11 a_{11} a11 的地址,那么以行为主序优先存储的地址计算公式为:

L o c ( a i j ) = L o c ( a 11 ) + ( ( i – 1 ) × n + ( j − 1 ) ) × L Loc(a_{ij}) = Loc(a_{11})+((i –1)\times n+(j−1)) \times L Loc(aij)=Loc(a11)+((i–1)×n+(j1))×L

同理,以列为主序优先存储的地址计算公式为:
L o c ( a i j ) = L o c ( a 11 ) + ( ( j − 1 ) × m + ( i − 1 ) ) × L Loc(a_{ij}) = Loc(a_{11})+((j −1) \times m+(i -1))\times L Loc(aij)=Loc(a11)+((j1)×m+(i1))×L
推广至多维数组,在按下标顺序存储时,先排最右的下标,从右向左直到最左下标,而逆下标顺序则正好相反。

树 —— 树根为第一层,最大层数为树高/深度

在这里插入图片描述

  • 结点的层次。根为第一层,根的孩子为第二层,依此类推,若某结点在第 i 层,则其孩子结点在第 i+1 层。例如,图中,A 在第 1 层,B、C、D 在第 2 层,E、F 和 G 在第 3 层。
  • 树的高度一棵树的最大层数记为树的高度(或深度)。例如,图中所示树的高度为 3。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

线索二叉树

在这里插入图片描述

在这里插入图片描述

哈夫曼编码

如果要设计长度不等的编码,必须满足下面的条件:任一字符的编码都不是另一个字符的编码的前缀,这种编码也称为前缀码。

设有字符集 {a,b,c,d,e} 及对应的权值集合 {0.30,0.25,0.15,0.22,0.08},按照构造最优二叉树的哈夫曼方法:先 按权值从低到高,先取字符 c 和所 e 对应的结点构造一棵二叉树(根结点的权值为 ce 的权值之和),然后与 d 对应的结点分别作为左、右子树构造二叉树,之后选 ab 所对应的结点作为左、右子树构造二叉树,最后得到的最优二叉树(哈夫曼树)如图所示。

其中,字符 a 的编码为 00,字符 bcde 的编码分别为 0110011101

在这里插入图片描述
译码时就从树根开始,若编码序列中当前编码为 0,则进入当前结点的左子树;为 1 则进入右子树,到达叶子时一个字符就翻译出来了,然后再从树根开始重复上述过程,直到编码序列结束。

例如,若编码序列 101110000100 对应的字符编码采用图所示的树进行构造,则可翻译出字符序列 edaac

树和森林 —— 树的双亲表示和孩子表示

在这里插入图片描述
在这里插入图片描述

存储 —— 邻接矩阵、邻接表

在这里插入图片描述
在这里插入图片描述

遍历 —— DFS、BFS

最小生成树 —— Prim 算法和 Kruskal 算法

普里姆算法 构造最小生成树的过程是 以一个顶点集合 U = u 0 U={u_0} U=u0 作为初态,不断寻找与 U U U 中顶点相邻且代价最小的边的另一个顶点,扩充 U U U 集合直到 U = V U=V U=V 时为止
在这里插入图片描述
克鲁斯卡尔求最小生成树的算法思想为:假设连通网 N = ( V , E ) N=(V,E) N=(V,E),令最小生成树的初始状态为只有 n n n 个顶点而无边的非连通图 T = ( V , { } ) T=(V,\{\}) T=(V,{}),图中每个顶点自成一个连通分量。 E E E 中选择代价最小的边,若该边依附的顶点落在 T T T 中不同的连通分量上,则将此边加入到 T T T 中,否则舍去此边而选择下一条代价最小的边。依此类推,直到 T T T 中所有顶点都在同一连通分量上为止。

在这里插入图片描述

拓扑排序和关键路径

在这里插入图片描述
拓扑序列为 6,1,4,3,2,5

最短路径

单源点最短路径 —— Dijkstra 算法

在这里插入图片描述

每对顶点间的最短路径 —— Floyd 算法

查找

静态查找 —— 顺序查找、二分查找、分块查找

动态查找 —— 二叉排序树、平衡二叉树、B 树

哈希表

排序

在排序过程中需要进行下列两种基本操作:比较两个关键字的大小将记录从一个位置移动到另一个位置。前一种操作对大多数排序方法来说都是必要的,后一种操作可以通过改变记录的存储方式来避免。

各排序算法详见:十大排序算法
优学堂 算法

排序算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
快速排序 O ( n l o g 2 n ) O(n\ log_2\ n) O(n log2 n) O ( n l o g 2 n ) O(n\ log_2\ n) O(n log2 n) O ( n 2 ) O(n^2) O(n2) O ( l o g 2 n ) O(log_2\ n) O(log2 n)内部排序不稳定
归并排序 O ( n l o g 2 n ) O(n\ log_2\ n) O(n log2 n) O ( n l o g 2 n ) O(n\ log_2\ n) O(n log2 n) O ( n l o g 2 n ) O(n\ log_2\ n) O(n log2 n) O ( n ) O(n) O(n)外部排序稳定
堆排序 O ( n l o g 2 n ) O(n\ log_2\ n) O(n log2 n) O ( n l o g 2 n ) O(n\ log_2\ n) O(n log2 n) O ( n l o g 2 n ) O(n\ log_2\ n) O(n log2 n) O ( 1 ) O(1) O(1)内部排序不稳定
希尔排序 O ( n 1.3 ) O(n^{1.3}) O(n1.3) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)内部排序不稳定
插入排序 O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)内部排序稳定
冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)内部排序稳定
选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)内部排序不稳定

数组 基本有序 的情况下进行排序:

  • 最适宜采用插入排序,时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)
  • 对快速排序来说是 计算时间的最坏情况,时间复杂度为 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( n ) O(n) O(n)
    • 快速排序在 “基本有序” 输入上退化到最坏情况的核心在于 枢轴(pivot)选择不当导致分区极度不平衡,使得递归树从理想的二叉树退化为一条链;
    • 在简单实现中,若总是选择数组的第一个或最后一个元素作为枢轴,对于已经或几乎有序的数组,这些位置恰好是当前子数组的最小或最大值,导致 一次分区只剥离 1 个元素,剩余 n − 1 n-1 n1 个元素集中在另一侧,完全失去 “分治” 优势。

在理想情况下,如果每次划分都恰好选择当前子数组的 真中位数 作为枢轴,那么快速排序就能保证 完全平衡 的递归分治:每次将规模为 n n n 的问题分解为两个规模为 n / 2 n/2 n/2 的子问题,再加上一次 O ( n ) O(n) O(n) 的划分工作。递归关系和时间复杂度:

T ( n ) = 2 T ( n 2 ) + O ( n ) ⟹ T ( n ) = O ( n log ⁡ n ) . T(n) \;=\; 2\,T\bigl(\tfrac n2\bigr)\;+\;O(n) \quad\Longrightarrow\quad T(n)=O(n\log n). T(n)=2T(2n)+O(n)T(n)=O(nlogn).

快速排序的空间复杂度:快速排序在变量(如基准值、索引和用于交换的临时变量)上使用恒定的额外空间 O ( 1 ) O(1) O(1)每次递归调用都会消耗栈空间,每层调用保存 O ( 1 ) O(1) O(1) 变量。

  • 在平衡划分的情况下,递归深度为 O ( l o g n ) O(log\ n) O(log n),即 平均空间复杂度为 O ( l o g n ) O(log\ n) O(log n)
  • 在不平衡划分的情况下,递归深度可以达到 n n n,即 最坏空间复杂度为 O ( n ) O(n) O(n)

在这里插入图片描述
对 A 进行简单选择排序时,第一趟需交换 21 和 17,导致 21 与 21* 的相对位置发生变化。

在这里插入图片描述

  • 该数组 基本有序,所以采用 直接插入 排序,需要进行的比较次数最少;
  • 初始有序数组为 {1},后1与前1比较,不交换,1, 1;2 与 1 比较,不交换,1, 1, 2;4 与 2 比较,不交换,1, 1, 2, 4;7 与 4 比较,不交换,1, 1, 2, 4, 7;5 与 7 比较,交换 5 和 7,继续往前比较,1, 1, 2, 4, 5, 7;5 与 4 比较,不交换,结束。
  • 注意最后的 5 不仅与 7 比较,还要与 4 比较。

在这里插入图片描述

  • 对于 A, B 选项,以 A 为例, a 1 , a 2 . . . , a n a_1, a_2 ..., a_n a1,a2...,an 均比 b 1 b_1 b1 小,那么它们各自与 b 1 b_1 b1 比较一次即可,剩下有序数组 B 不需要比较直拼接到 a n a_n an 后即可。共需 n n n 次比较。
  • 同理 D 选项的 b i / 2 + 1 b_{i/2+1} bi/2+1 b n b_n bn 都不需要比较直接拼接。
  • 对于 C 选项, a 1 a_1 a1 b 1 b_1 b1 比较 a 1 a_1 a1 小,然后 a 2 a_2 a2 b 1 b_1 b1 比较 b 1 b_1 b1 小,继续 a 2 a_2 a2 b 2 b_2 b2 比较 a 2 a_2 a2 小,继续。。。
排序算法平均时间复杂度最好情况最坏情况空间复杂度排序方式稳定性
计数排序 O ( n + k ) O(n + k) O(n+k) O ( n + k ) O(n + k) O(n+k) O ( n + k ) O(n + k) O(n+k) O ( n + k ) O(n+k) O(n+k)外部排序稳定
桶排序 O ( n + k ) O(n + k) O(n+k) O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2) O ( n + k ) O(n + k) O(n+k)外部排序稳定
基数排序 O ( n ∗ k ) O(n * k) O(nk) O ( n ∗ k ) O(n * k) O(nk) O ( n ∗ k ) O(n * k) O(nk) O ( n + k ) O(n + k) O(n+k)外部排序稳定

若一组 大规模 待排序记录的 关键字取值均在 0-9 之间,则适宜采用:计数 排序。

相关文章:

  • 全面学习c++类与对象(中)(非常重要)(析构构造拷贝函数赋值运算符重载等等)
  • SOC-ESP32S3部分:7-如何学习ESP32S3-IDF开发
  • PHP7内核剖析 学习笔记 第七章 面向对象
  • 如何减⼩dockerfile⽣成镜像体积?
  • ollama+open-webui搭建可视化大模型聊天
  • Android-MVVM框架学习总结
  • BERT框架:自然语言处理的革命性突破
  • 服务器操作系统调优内核参数(方便查询)
  • SDWebImage源码学习
  • vue组件渲染到iframe里面(同域名下),组件可以在同一项目下维护
  • Linux 部署 RocketMQ
  • oracle数据库查看当前session会话数、查询最大支持连接数、修改最大支持连接数
  • 服务器并发实现的五种方法
  • MCP Server Resource 开发学习文档
  • WebGL基本概念
  • CUDA 加速的基础线性代数库cuBLAS
  • CC53.【C++ Cont】一维前缀和
  • AI编程: OpenAI Codex vs Google Jules vs GitHub Copilot++
  • Java转Go日记(五十四):gin路由
  • 【笔记】修复AttributeError: ‘super‘ object has no attribute ‘__del__‘
  • 黄页网站系统/企点
  • dw代码做网站/百度seo详解
  • 杭州网站建设公司哪家好/seo关键词怎么填
  • 工业设计公司简介/行者seo无敌
  • python做网站有优势/seo主要是指优化
  • 宝安各大网站制作比较好的/搜索引擎推广排名