408 数据结构
常用名词
存取
- 一般是用作计算机术语,存取是一个动作合成词,存指代计算机的写入,取指代计算机的读取。
第一章-绪论
数据结构的基本概念
数据的存储结构
顺序存储
链式存储
索引存储
散列存储
数据类型和抽象数据类型
- 数据类型是一个值的集合和在此集合上的一组操作的总称。
- 抽象数据类型不考虑物理结构(存储结构)
知识总览
学习方式顺序
算法的基本概念
时间复杂度
空间复杂度
第二章-线性表
总览
顺序表
顺序表的定义
知识结构
静态分配
动态分配
顺序表的特点
顺序表的插入删除
第三章-栈和队列
队列
顺序存储的循环队列
- 队列元素长度:(rear+MaxSize-front) %MaxSize
front指向队头,rear指向下一个元素入队的位置(队尾元素的下一个位置)
判断队列已满
- 方法一:用一个空闲空间
(Q.rear+1)%MaxSize == Q.front
- 方案2:size记录大小
- 方案3:tag记录最近的操作是入队(导致队满)还是出队(导致队空)
判断队列为空
- Q.front = Q.rear
入队
front指向队头,rear指向队尾元素
入队
队列初始化
- front = 0,rear = n-1
判空
判满
-
方案一:牺牲一个存储单元
(Q.rear+2)%MaxSize == Q.front;
-
方案二:增加辅助变量
栈的应用
中缀表达式转后缀表达式
第四章——串
KMP算法
求next数组
- 串的前缀:包含第一个字符,且不包含最后一个字符的子串。
- 串的后缀:包含最后一个字符,且不包含第一个字符的子串。
- next [j] = 前1~j-1个字符组成的串S的最长相等前后缀长度 + 1。
第五章-树
数的定义和基本术语
- 特性:
- 定义:
常见考点
结点数和总度数
-
- 结点数 = 总度数(总边数)+1
度数为m的树
-
- 度为m的树:各结点的度(边数)的最大值
- 至少有一个结点度数为m
- 一定是非空树,至少有m+1个结点
m叉树
-
- m叉树:每个结点最多只能有m个孩子的树
- 任意结点的度小于等于m
- 允许是空树
第i层的结点数最多有
高度为h的m叉树最多有
高度为h的m叉树最少有h个结点
具有n个结点的m叉树的最小高度
二叉树
二叉树的五种状态
几个特殊的二叉树
满二叉树
完全二叉树
二叉排序树
平衡二叉树
二叉树选择题重要考点
二叉树的存储结构
二叉树顺序存储
- 二叉树顺序存储结构,只适合存储完全二叉树
二叉树的链式存储
- n个结点的二叉链表共有n+1个空链域。
二叉树的先中后序遍历
先序遍历
- 根节点—左子树—右子树
中序遍历
- 左子树—根节点—右子树
后序遍历
- 左子树—右子树—根节点
求二叉树深度
总结
由遍历序列构造二叉树
前序 + 中序遍历序列
层序+中序遍历序列
线索二叉树
中序线索化
先序线索化
后序线索化
线索二叉树找前后继
中序线索二叉树找后继
中序线索二叉树找前驱
先序线索二叉树找先序后继
先序线索二叉树找先序前驱
后序线索二叉树找后序前驱
后序线索二叉树找后序后继
- 不能找到父节点
- 能找到父节点,三叉链表
知识回顾
二叉排序树
二叉排序树的查找
二叉排序树的插入
二叉排序树的删除
- 情况1:若被删除结点z是叶结点,则直接删除,不会破坏二叉排序树的性质。
- 情况2: 若结点z只有一颗左子树或右子树,则让z的子树成为z父节点的子树,替代z的位置。
- 情况3:若结点z有左、右两颗子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删除这个直接后继(或直接前驱),这样就转换成了第一种情况。
平均查找长度
知识回顾
平衡二叉树
最小不平衡子树
调整最小不平衡子树(LL)
调整最小不平衡子树(RR)
LL与RR代码
调整最小不平衡子树(LR)
调整最小不平衡子树(RL)
高度为h的平衡二叉树至少需要多少个结点
假设F1,F2为TL,TR的最少节点数,则,F(n) = F1+F2 +1。那么F1,F2 到底等于多少呢?
由于TL,TR与T一样是平衡二叉树,又由于我们知道T的最少节点数是F(n),其中n为T的高度,因此如果我们知道TL,TR的高度就可以知道F1,F2的值了。
由平衡二叉树的定义可以知道,TL和TR的高度要么相同,要么相差1,而当TL与TR高度相同(即:都等于n-1)时,我们算出来的F(n)并不能保证最小,两边都是(n-1),明显比(n-2)大,节点数更多
因此只有当TL与TR高度相差一(即:一个高度为n-1,一个高度为n-2)时,计算出来的F(n)才能最小。
此时我们假设TL比TR高度要高1(即:TL高度为n-1,TR高度为n-2),则有:F1 = F(n-1),F2 = F(n-2)。
因此得到结论:F(n) = F(n-1) + F(n -2 ) + 1
总结
树的存储结构
双亲表示法
- 优点:查找指定结点的双亲很方便。
- 缺点:查指定结点的孩子只能从头遍历。
孩子表示法
- 优点:找孩子方便。
- 缺点:找双亲不方便。
孩子兄弟表示法
森林和二叉树的转换
- 二叉树的右指针指向兄弟结点。
- 二叉树的左指针指向孩子结点。
森林、树、二叉树的遍历
哈夫曼树
思想
- 使权重(代表出现次数或概率)最多的元素占用最短的路径(代表操作次数)。
带权路径长度
- 结点的带权路径长度:从树的根到该结点的路径长度(经过的边数)与该结点上权值的乘积。
哈夫曼树定义
哈夫曼树:带权路径长度(WPL)最小的二叉树称为哈夫曼树,也就是最优二叉树。
哈夫曼树的构造
前缀编码
- 各个字符出现的频度作为权值,编码长度作为路径长度。
第六章-图
图的基本概念
图的定义
无向图
有向图
简单图
顶点的度
- 无向图:顶点v的度是指依附于该顶点的边的条数,记为TD(v)。
- 有向图
- 入度:以顶点v为终点的有向边的数目。 ID(v)。
- 出度:以顶点v为起点的有向边的数目。OD(v)。
- 有向图的入度之和 和 出度之和相等。
顶点与顶点的关系描述
连通图、强连通图
- 无向图中,若从顶点v到顶点w有路径存在,则称v和w是连通的。
- 有向图中,若从顶点v到顶点w和从顶点w到顶点v之间都有路径,则称这两个顶点是强连通的。
子图、生成子图
- 生成子图:包含了原图的所有点。
连通分量、极大连通子图(无向图)
- 极大连通子图(连通分量):必须连通,且包含尽可能多的顶点和边。
强连通分量、极大强连通分量(有向图)
生成树(极小连通子图)
- 连通图的生成树是包含图中全部顶点的一个极小连通子图。
边的权、带权图/网
几种特殊形态的图
邻接矩阵
- 缺点:空间复杂度高,不适合存储稀疏图。
求顶点的度
- 有向图中:A元素的行对应A元素的出度,A元素的列对应A元素的入度。
带权图
邻接矩阵的性质
- A^n的元素A ^n[i][j] = 由顶点i到顶点j的长度为n的路径的数目。
邻接表
邻接表定义
计算度、出度、入度
- 无向图:度,遍历行
- 有向图:
- 出度:遍历行
- 入度:难算,遍历整个图
对比
十字链表
- 只适合有向图
邻接多重表
- 只适合无向图
最小生成树
普利姆算法
Kruskal算法
时间复杂度对比
求最短路径
Dijstra算法
- 求一个顶点v0到所有其他顶点的最短路径。
Floyd算法
- 求出所有顶点之间的最短路径
对比
有向无环图(DAG)
定义
AOV网
- 顶点表示活动
拓扑排序
逆拓扑排序
AOE网
- Activity on Edge Network
- 顶点表示事件(一瞬间发生),边表示活动。
求关键路径
1.求事件vk的最早发生时间 ve(k)
- 事件vk的最早发生时间:
- 源点v1到顶点vk的最长路径长度。
- 决定了从v1开始的活动的最早发生时间。
- vj 为 vk的任意前驱
2.事件vk的最迟发生事件 vl(k)
- 该事件最迟必须发生的时间
- vj为vk的任意后继
3.活动的最早发生时间
4.求所有活动的最迟发生时间
5.e(i)=l(i)的即为关键路径
关键路径特性
第七章-查找
平均查找长度
- 衡量查找算法效率最主要的指标
顺序查找
实现
无序表ASL
有序表ASL
折半(二分)查找
实现
查找效率分析
折半查找判定树
- 左子树比右子树多一个元素(或少一个元素)。
折半查找的查找效率
- 折半查找判定树的树高为:
分块查找
思想
用折半查找查索引
- 要在low所指分块中查找
查找效率分析
红黑树
为什么要发明红黑树
红黑树的定义
- 左子树结点值<= 根结点值 <= 右子树结点值 (左根右)
- 根结点和叶节点是黑色的(根叶黑)
- 不存在两个相邻的红结点
- 黑路同
红黑树的性质
红黑树的插入
B树
B树的核心特性
n个关键字把数值区域切分成n+1个部分
B树的高度
最小高度(每个结点的关键字和分叉最多)
最大高度(每个结点的关键字和分叉最小)
B树的插入
B树的删除
删除结点在终端结点(关键字数足够)
删除结点在非终端结点(关键字数足够)
关键字数不足够
兄弟够借
兄弟不够借(合并)
B+树
散列查找
散列表(哈希表)定义
散列函数的构造方法
数字分析法
除留余数法
直接定址法
平方取中法
处理冲突的方法
开放地址法
线性探测法
缺点:
平方探测法
伪随机序列法
再散列法
删除注意事项
拉链法(简单)
第8章—排序
插入排序
插入排序
折半插入排序
总结
希尔排序
冒泡排序
算法实现
算法性能分析
快速排序
代码
时间复杂度
简单选择排序
堆排序
堆的概念
建立大根堆
- 代码:
堆排序的完整逻辑
时间复杂度分析
稳定性
在堆中插入新元素
在堆中删除元素
插入删除总结
归并排序
代码实现
复杂度分析
外部排序
时间开销分析
- 外部排序时间开销 = 内部排序的时间 + 读写外存的时间 + 内部归并的时间
- 减少归并趟数来减少读写外存的时间。
优化1:多路归并
优化2:减少初始归并段数量