一.查找的基本概念
- 什么是关键字?唯一区分各个元素 的数据项,使用基于关键字查找,查找结果应该是唯一的
- 什么是静态查找表?仅查找符合条件的元素
- 什么是动态查找表? 除了查找速度,也要关注插/删操作是否方便实现
- 查找算法的评价指标是什么?平均查找长度ASL (所有查找过程中的关键字的比较次数的平均值),评价查找算法效率的时候通常考虑查找成功/失败两种情况的ASL
二.查找算法
1.顺序查找
- 查找过程? 使用顺序表的数据结构,按数组下标遍历顺序表 将关键字key与每一个元素比较,直到找到关键字,返回下标。
- **查找效率是多少?**O(n)
2.折半查找
- 什么是折半查找? 又称"二分查找",仅适用于有序 的顺序表
- 查找效率如何?使用折半查找判定树,这个判定树只能保证右子树节点数-左子树节点数=0或1,折半查找判定树一定是平衡二叉树,树高h=(log2(n+1))向上取整(查找成功)。有n+1个失败结点(就是空链域的数量),如果包含失败节点那么树高是h+1。
- 不是任何情况下折半查找都比顺序查找优秀
- 如果mid等于向上取整呢,判定树有什么变化?左子树节点数-右子树节点数=0或1
- 折半查找代码?
cpp
typedef struct{
int* elem;//动态数组基址
int tableLength;//查找表的长度
}STable;
int Binary_Search(STable S,int key)
{
int low=0,high=S.tableLength-1;
while(low<=high)
{
int mid=(low+high)>>2;//折半
if(key>S[mid])
{
low=mid+1;
}
else if(key<S[mid])
{
high=mid-1;
}
else
{
return mid;//相等即找到
}
}
return -1;//没找到
}
3.分块查找
- 分块查找的思想是什么? 在索引表中查找key所在的分块 ,可以顺序查找也可以折半查找,在块内顺序查找
- 分块查找的特点? 索引表存放每一个分块的起始终止地址和最大关键字 ,各个分块保证块内无序,块间有序
- 对于在索引表中折半查找的时候 ,如果索引表不包含目标关键字 ,则折半查找索引表最终停在low>high的位置,要停在low所指的索引分块(因为索引表存放的是每个分块的最大关键字)
- 如果想要顺序查找索引表时ASL最小,那么怎么划分n个元素? 划分为个块,每个块内有个元素。ASL最小=+1
4.二叉排序树(二叉搜索树)
- 二叉排序树有什么性质? 左子树上所有结点的值均小于右子树所有结点的值;右子树上所有结点的值均大于左子树所有结点的值;左子树和右子树又是一棵二叉排序树。
- 中序遍历二叉排序树会得到什么结果? 一个递增序列
- **二叉排序树的递归查找和循环查找有什么区别?**递归查找的空间复杂度为O(h),循环查找的空间复杂度为O(1)
- **二叉排序树递归插入关键字为k的新结点的空间复杂度?**最坏是O(h)
- 二叉排序树删除结点的时候有多少情况?(1)删除的结点是叶子结点,直接删除即可(2)删除的结点是只有左子树或者只有右子树的结点,删除之后替代(3)删除的结点有左右子树,选择左子树的最大值/右子树的最小值替代
- 查找成功的平均查找长度是多少?(log2n)向下取整+1,平均查找长度O(log2n),查找失败则需补充失败结点,无论是插入还是删除,都需要查找,所以有必要优化平均查找长度
5.平衡二叉树
- **什么是平衡二叉树?**简称平衡树,AVL树,树上任意结点的左子树和右子树高度之差不超过1
- 什么是平衡二叉树结点的平衡因子? 结点平衡因子=左子树高 -右子树高,平衡二叉树的平衡因子只可能是-1,0,1
- 平衡二叉树插入之后如何调整"最小不平衡子树"? 从插入点往回找到第一个不平衡结点 ,调整以该结点为根的子树
- 如何调整最小不平衡子树?(引用王道视频图片)
- 平衡二叉树的查找数量级是o(log2n),比顺序查找优秀,与折半查找相当
就平衡二叉树查找数量级而言,其与折半查找的判定树数量级相同为log2n。但是,对于折半查找使用顺序表是对于表的增/删复杂度为O(n),但是平衡二叉树增/删只需要修改其指针即可。所以折半查找适合于静态查找表,平衡二叉树适合于动态查找表。
6.红黑树
- 红黑树与平衡二叉树相比有什么优点?红黑树与平衡二叉树的查找/删除/增加的数量级都是log2,但是平衡二叉树的平衡特性很容易被破坏,需要频繁调整树的形态,时间开销大。而红黑树的插入删除很多时候不会破坏"红黑"特性,一般都可以在常数级的时间内完成调整。
- 如果是以查找为主,那么适合用平衡二叉树。如果适合于频繁插入删除的场景,红黑树实用性更强
- 红黑树的特点/性质?
(1)结点定义
cpp
typedef struct{
int value;
RBnode* parent;//红黑树结点的双亲结点
RBnode* lchild;
RBnode* rchild;
int color;//红黑树每个结点只有一个颜色0黑1红
}RBnode;
(2)红黑树是二叉排序树
(3)每个结点或是红色的或是黑色的,根节点和叶子结点是黑色的
(4)不存在两个相邻的红结点(即红结点的父结点和孩子结点均是黑色)
(5)对于每一个结点,从该结点到任一结点的简单路径上,所含黑结点数目相同
- 什么是红黑树结点的"黑高"属性? 从该结点出发,到达空结点时所经过的黑结点的数目
- **红黑树都有哪些特性?**从根结点出发到叶子结点的最长路径的长度不大于到最短路径长度的两倍;有n个内部结点的红黑树高度h<=2log2(n+1)
- 红黑树的插入过程?
- 首先查找,确定插入位置,原理等同于二叉排序树,插入新结点
- 新结点是根,染为黑色;新结点非根,染为红色(红结点不会增加黑路的长度)
- 若插入结点后依然满足红黑树定义(没有两个连续的红结点),那么插入结束
- 若插入新结点之后不满足红黑树的定义,需要调整,使其重新满足红黑树的定义
如果插入的结点的叔叔颜色是黑色:那么旋转+染色。(从爷结点开始)LL:右单旋,父换爷+染色(红黑互换);RR:左单旋,父换爷+染色;LR:左右双旋,儿换爷+染色;RL:右左双旋,儿换爷+染色
如果插入的结点的叔叔颜色是红色:那么染色+变新。叔父爷染色,爷变为新结点
- 红黑树的删除操作时间复杂度是多少?O(log2)
- 红黑树删除操作和二叉排序树的删除操作一样,删除结点后,可能破坏"红黑树特性",需要调整结点颜色、位置,使其再次满足红黑树特性。
7.B树
- 什么是B树?B树又称多路平衡查找树,B树中所有结点中孩子个数的最大值称为B树的阶,通常用m表示。一棵m阶B树或为空树,或为满足如下特征的树。
- B树都有什么特征?
所有的叶子结点都出现在同一层上,并且不带信息(可以看做是外部结点或查找失败的结点,实际上这些结点不存在,指向这些结点的指针为空)。
一个结点中所有关键字升序排列,两个关键字K1和K2之间的所有结点的关键字在K1,K2之间
cpp
int *keys; //存储关键字的数组
int t; //最小度 (定义一个结点包含关键字的个数 t-1 <= num <= 2t -1)
BTreeNode **C; //存储孩子结点指针的数组
int n; //记录当前结点包含的关键字的个数
bool leaf; //叶子结点的一个标记,如果是叶子结点则为true,否则false
8.B+树
- B树和B+树有什么区别?
- (1)B+树的非叶子节点不保存关键字的指针,这样使得非叶子结点有更大的空间存放关键字(2)B+树的叶子结点包含了所有关键字的指针,所以每次查询都要查询到叶子结点,每次查询的次数也都一样
- (3)B+树叶子结点的关键字从小到大有序排列,左边结尾数据都会保存右边开始数据的指针
- (4)非叶子结点的子结点数等于关键字数
- B+树和B树相比有什么优点?
- B+树由于不在非叶子结点上存放指针,所以有更大的存储空间存放key;与之关联的叶子结点存放的数据也更加紧密,有更好的空间局部性。缓存命中性更好。
- 由于B+树叶子结点的各个结点链接相连,所以遍历B+树只需遍历叶子结点链表即可。而遍历B树需要层层遍历,B+树相比来说更快。
- **B+树的查询?**从根结点开始查找,逐层向下查找,直到找到key对应的叶子结点
- B+树的插入?
- B+树的插入要从叶子结点插入,但是要保证叶子结点中关键字的顺序性
- 由于B+树关键字中有明确的顺序,所以插入时可能会导致超过父结点关键字的大小,称为"上溢"。
- 如果插入的结点比父结点关键字大,那么需要父结点更新关键字值,叶子结点进行"分裂"
- **B+树的删除?**B+树的删除可能会导致"下溢",即当前节点的关键字个数小于2