目录
(1)if根节点黑高=bh,则从根开始pathmax<=2pathmin
探测覆盖率(informal):在偏移的时候能覆盖散列表的程度
一、基本概念
1.概念
查找、查找表、关键字(唯一性)
2.操作
查找、插入、删除
只执行查找操作的表称为静态查找表,else动态查找表
3.评价查找算法
查找长度、平均查找长度(ASL)、考虑成功and失败时的ASL
二、顺序查找
演示操作:html演示
1.思路
循环 查询
or 运用哨兵:将带查找ele放置0号,当遍历到0号时说明未找到
优点:不用考虑越界问题
2.实现
ASL成功:(1+2+……+n) / n= (n+1)/2
ASL不成功:n+1
3.优化
1)有序表
生成查找判定树,左小右大,当ele处于某一位置时结束查询,可以降低ASL不成功
eg:7 13 19 29 37 43 找21
查找判定树如下:
so当查21时,查找29说明小于,则为(19,29)之间,so查找4次,即父节点的高度
总结:查找成功,查找次数为节点的高度;查找失败,查找次数为节点的高度
2)每个ele被查找的概率不同
将概率大的放前面,可以降低ASL成功
eg:7 13 19 29 37 43 找21
查找概率:15% 5% 10% 40% 28% 2%
改动前:ASL成功=1*0.15+2*0.05+……+6*0.02=3.67
->29 37 7 19 13 43
ASL成功=(1*0.4+2*0.28+……+6*0.02) /6 = 2.18
4.Tn
O(n)
三、折半查找
1.思路
折半查找只适用于有序的顺序表
因为顺序表可以随机查找
2.实现(不考虑相等ele)
三个指针,low,high,mid,low指向第一个ele,high指向最后一个ele,mid指向中间,依次判断mid与待找ele大小,根据大小移动low or high,循环
3.查找判定树
(1)构建
用mid将arr分成左右两个部分,右子树个数-左子树个数<=1
(2)特性
1)查找判断树一定为平衡的BST
每次中分,肯定结点个数之差<=1
2)只可能最底下一层不满
3)查找表的n个关键字,有n+1个失败结点
n个结点时n+1个空指针
4)树高h = log2(n+1) up
4.Tn
O(log2n) ~ O(logn)
四、分块查找(索引顺序查找)
1.思路
将整个arr分成若干块,块内无序,块间有序
分成两个部分 :索引表+小arr
记录索引表中每个分块max的keyword作为分块区间
2.实现
∵索引表之间有序,so可以使用二分or顺序查找
先判断带查找ele在哪个部分,再在小arr中遍历查询
又∵没有规律的话不好计算,so一般考的都是有规律的
将整个arr分成b块,每块有s个ele
4.ASL成功
ASL = 查找索引表+查找小arr
(1)顺序查找索引表
(1+2+3+……+b)/b+(1+2+3……+s)/s = (b+1)/2+(s+1)/2 = (b+s)/2 + 1
当s = n^(1/2)时ASLmin(求导推导)
(2)折半查找索引表
log2(b+1) up + (s+1)/2
5.易错点
if折半查找时待查找ele与索引表中ele不相同,则在low指向的索引表指向的小arr中寻找
五、二叉排序树(二叉查找树)(BST)
1.定义(与二叉树相同)
左小右大
2.创建
插入ele,与查找思路相同,与已插入结点比较,if e 小,递归lchild
3.输出
中序遍历,检查是否创建成功,升序
4.查找
(1)非递归
从根节点开始比较,if待查找ele==该结点,then ==,if 待查找ele > 该结点,则查找右孩子,if<则查找左孩子,if需要找目标结点的父节点,则在开始时可以使用parent记录T即可
(2)递归
从根开始比较,ife小,则递归比较lchild,if e 大,递归比较rchild
5.插入(与创建时插入相同)
6.删除
(1)叶子结点
直接删除
(2)一个孩子
该孩子代替被删除结点
(3)两个孩子
用待删除结点的前驱or后继顶替该结点
前驱:左子树最右下结点
后继:右子树最左下结点
reason:保证前小后大
7.查找效率分析
(1)Tn
取决于树的高度
Tn max = O(n) Tn min = O(log2n)
(2)ASL
ASL成功=1*1+2*2+……+4*3 / 10 = 2.9
ASL失败=3*5+4*6 /11 = 3.5
6.考点(难想到的)
与卡特兰数结合:有n个不同结点,问树形态多少个 1/(n+1) * C2n(n)
reason:卡特兰数应用场景 (利用栈,求顺序)
1.n个ele入栈,问出栈之后的顺序
2.n个结点,BSL树形态(中序遍历的顺序个数)
3.括号匹配,n对括号,有多少种匹配序列
六、平衡二叉树(AVL树)(手算)
1.定义与特性
每个结点|左右子树高度差|<=1
平衡因子
2.调整(*)
从下往上找不平衡子树,找最上面三个结点,进行调整,别的结点照抄+BST插入
再进行一次调整之后再次检查,看是否别的结点仍平衡
attn:当子树高度下降,父及祖先结点可能会不平衡
3.插入
1)已有树中插入新结点
BST插入+调整
2)空树中依次插入多个结点
依次BST插入,每次插入之后检查是否平衡,if不平衡调整
eg: 15 3 7 10 9 8
逐步解析:橙色底表示待调整子树
step1:15.3.7首次不平衡,直接按照左小右大调整
step2:插入9的时候,3.10.9不平衡,直接按照左小右大调整
step3:插入8时,根节点不平衡,so沿着8找最上面3个结点,so调整15.9.3
15.9.3按照左小右大调整,与这些结点相连的直接照抄,断开的结点按照BST插入
4.删除
BST删除+调整
5.查找
BST查找
6.使用场景
仅查询,静态查询表
7.考点
(1)每个非叶结点平衡因子=1,说明树的结点数min
推导:h为树高,T为结点个数
level Tmin
1 1
2 2
3 4
h Th-1 + Th-2 + 1
so Th = Th-1 + Th-2 + 1 表示if树有h层,最少结点数与前两层之间的关系
(2)已知树的高度,求结点数量min
七、红黑树(RBT)(会手画)
1.背景
∵AVL树插入删除ele之后需要保证平衡,so使用红黑树
2.定义(*)
特殊的二叉排序树,左大右小
左根右,根叶黑,不红红,黑路同
3.性质(*)
(1)if根节点黑高=bh,则从根开始pathmax<=2pathmin
"不红红,黑路同",sopathmax肯定红黑交叉,pathmin全是黑的,但黑肯定<红黑个数一半
性质(1)推论:当树高=h时,树黑高>=h/2
(2)树高度h,结点数n,h>=2log2(n+1)
当根节点黑高= bh时,树结点数n>=2^bh -1
reason:性质(1)推论,so n>=2^(h/2)-1
4.插入(*)
插入结点看作红色结点,在插入时只用检查“不红红”。不符合条件时进行调整
看叔叔脸色
if叔叔是红色,则父节点、叔叔和爷爷结点换色,if爷爷是全树的根节点,then仍为黑色
if叔叔是黑色,则按照AVL查找的方法,调整,调整之后子树根变为黑色,左右孩子变成红色
最后补null
eg:3.10.7 .5.4.12
逐步解析:
step1:插入5,为红,叔叔为红,上三角换色,因为7为总根,so为黑
step2:插入4,叔为黑(黑null),soAVL调整,3 . 5 .4,调整之后父为黑,子为红
step3:补黑null
5.删除(不考)
6.查找
与BST、AVL树相同
7.使用场景
仅查找,AVL;查找删除多的,RBT
八、B树(手算)
1.背景
由BST引出m叉树,m叉树表示最少有一个顶点有m个子树
但在m叉树操作中效率较低,因此对m叉树进行优化,产生B树
2.概念(要点)
(1)m阶B树表示一个结点最多有m个分支
attn:最多表示可以没有,考点:判断树的类型
(2)顶点中包括关键字
关键字表示分割线,
attn:根节点可以有多个关键字
(3)最底层表示叶子结点,空指针域
叶子结点个数表示最后分割成的总个数
3.性质
(1)左小右大
因为从BST来的
(2)分支最少m/2 up,关键字最少m/2 up -1
保证树不变成细高的,∵树的查找效率是由树的高度h决定
关键字两边是分支,so -1
(3)每棵子树高度相同 <==>叶子结点都在一层
AVL树差度-1太麻烦了,直接保证绝对平衡
(4)树高度h与结点个数n关系
推导:根据结点数
attn:h表示整棵树的高度,n表示总结点数
做题的时候可以灵活使用,if问第n个结点最大最小高度,则直接使用这个公式也可以,此时理解为n为该树前n个结点构成此树的第几层
4.查找
从根开始查找,根据左小右大,在每个顶点处可以顺序or二分查找,找到就停止
attn:整体不能称为顺序查找,因为顺序查找适用于线性存储结构,树是树形存储结构
5.插入(构建)
整体从下往上插入,根据左小右大,溢出了就从m/2 up处裂开,此处关键字扔到上层变成父结点,后面的变成兄弟结点中的keyword
step1:计算m/2 up = 2
step2:插入+检查
1)eg:插入99
2)插入87
3)插入75
逐步分解:
step1:移动73,到目标地方,但是父节点也溢出,so父节点裂开
step2:移动80
step3:检查整体是否左小右大
6.删除
删除时按照BST原则,so删除任何结点一定转化为终端结点
借keyword和检查按照每个结点最少的keyword原则
situation:
(1)左(右)兄弟够借
if一方够借,则借一个,但是直接借左小右大原则破坏,so与父结点中的keyword一块换
(2)都不够借
与父节点中keyword、一方整合
eg:以右方为eg:右方结点中kw和这两个的父节点中适合的kw结合,空结点删除,合并之后检查
(3)step
step1:计算kw min
step2:按照BST删除,左小右大调整
step3:检查,是否还需要调整
(4)eg:5阶B树
1)删除80(转终端结点)
2)删除38(右兄弟够借)
逐步分析:
step1:计算kw min = 5/2 up -1 = 2
step2:删除38,删除之后25.38这个结点kw不符合要求,右兄弟3个够
step3:但是直接70->38不符合左小右大,so父亲第一kw到左孩子,右孩子第一个kw到父节点第一个kw
step4:检查
3) 删除49
逐步分析:
step1:kw min =2
step2:删除49,kw不符合要求,右兄弟不够借,父第一个kw、右兄弟合并
step3:父节点kw不符合要求,右兄弟不够借,父第一个kw、右兄弟合并
step4:检查
九、B+树(概念)
B树+分块查找
相当于B树的应用,if我们需要进行索引操作,那么需要找到最后一个ele(B+的叶子结点)
1.概念
n阶B+树表示一个结点最多有几个子树,so 结点:kw == 1:1
2.查找
中途遇到相同的时候不能停,需要查到叶子结点
reason:分支结点仅表示索引,不带信息
distinguish:B查到相同的就可以停止,B树分支结点也带信息
3.应用
MySql的索引应用的就是B+树的原理
4.考点
见十
十、B树 vs B+树
B+常这样考察
1.相同点
(1)都可以随机存储
构建树的本质就是能够随机查找ele,(集合)
(2)keyword数量 m/2 up -1
(3)等高
绝对平衡
2.不同点
(1)B+还可以顺序查找
B+树叶子结点直接是顺序存储的,so可以顺序查找
(2)B树每个结点包含信息,B+只有叶子结点包含信息
(3)关键字:分支
B树n:n+1,B+树1:1
十一、散列表
1.概念
(1)散列表 and 散列函数
散列表:哈希表
散列函数:映射关系
(2)冲突 and 同义词
多个关键字映射到了一个地址,产生了冲突,彼此之间称为同义词
(3)聚集(堆积)
因为产生冲突,so需要解决冲突,由于解决冲突算法的好坏,因此可能使同义词分布在目标的附近很长空间,因此查找时效率降低,这种现象叫做聚集or堆积
(4)装填因子(负载因子) and 存储效率
装填因子:散列表中记录数 / 散列表能容纳的总记录数
存储效率:能够放置记录的数量 / 散列表总长度
so可以用装填因子表示
2.构建散列函数
(1)target
减少关键字映射到同一个地址的情况
(2)attn
1)能囊括所有关键字(定义域为R)
2)散列函数值域<=地址范围
3)均匀分布
4)尽可能简单
(3)方法
1)除留余数法
取余,余数选择:靠近地址长度的质数
最常用的算法,只要是整数即可
2)直接定址法
一串连续的数,数值 - 第一个数
so相当于从0开始
H(key) = key - 第一个ele
eg : 10 11 12 13 14 15 16
H(key) = key - 10
0 1 2 3 4 5 6
3)数字分析法
关键字有几位分布均匀,so根据这几位分布均匀的数字进行定址
4)平方取中法
每一位都不均匀,so将kw平方之后取中间几位
reason:中间几位收到每一位的影响
3.解决冲突
(1)拉链法
所有映射到一个地址的所有kw用指针连起来
1)插入
step1:根据映射函数找target地址
step2:头插法(default)or尾插法
优化:在插入时保证有序,查询时可以使用二分查找,效率略微提高
2)查找
step1:根据映射函数找target地址
step2:依次遍历所有eles
分析查找长度:查找长度是与kw查找的次数,不计算空指针(default)
attn:if算了一遍没有,确定计算过程及结果是正确之后没有,再算上空指针计算一遍
3)删除
查找+删除
(2)开放地址法
1)原理
在发生冲突时候,让同义词放置大周围弄地址处
2)操作
i)插入
计算目标地址+解决冲突,直到有null地址
ii)查找
计算目标地址+解决冲突,直到有相同(查找成功)or null(查找失败)
iii)删除
计算目标地址+解决冲突,直到有相同(之后删除)or null(查找失败进而删除失败)
attn:不能物理删除,因为if置null了,后面就没有办法到了,之后就无法删除
解决措施:逻辑删除,设置一个删除标志,so当删除后面的eles时接着查询即可
存在缺陷:长此以往,全是空标志,但是实际没有data
解决措施:定期删除空位置关键字,移动由于冲突被移动的kw
3)常用方法(计算di -- 第i次移动时的偏移量)
i)线性探测法
依次往右走
ii)平方探测法
依次移动 (-1)^n i^2 -- 左右摇摆
iii)双散列法
利用两个散列函数
iv)伪随机序列法
coder DIY
attn:所有算法在实际使用时都是循环的,当到了散列表尾部可以回来
4)拓展:计算探测覆盖率
探测覆盖率(informal):在偏移的时候能覆盖散列表的程度
i)线性探测法
能覆盖所有,最多移动n-1次
ii)平方探测法
左右移动,又是对称,so覆盖率>=50%,but不能全部
提高探测覆盖率的方法:令散列表长度m = 4j+3
iii)双散列法
利用两个散列函数hash(key)、hash2(key) ,so不一定能全部覆盖
提高探测覆盖率的方法:让hash2(key)与m互质
做法:令m为质数 hash2(key) = m - (key%m)
so hash2(key) <= m ,就能查到所有地址
iv)伪随机序列法
coder DIY,so无法确定
4.性能分析
(1)影响散列表查找效率的因素
填充因子、散列函数、解决冲突算法
(2)计算ASL
ASL success
ASL failure = sum 可能映射到的地址*比较的次数 / 可能偏移的地址
attn:默认是不包括空指针,但是if选项没有,就算上空指针算一遍
eg:长度5散列表,函数H(k) = (k+4)%5,用线性探查再散列解决冲突,kw :2022.12.25,然后删除25,求ASL失败
A.1 B 1. 6 C 1.8 D 2.2
step1:计算target地址
step2:处理冲突
step3:删除25,attn:是逻辑删除,so遇到应继续
step4:计算ASL failure
1 1 4
2022 12 25(已删除)
0 1 2 3 4
ASLf = 0+2+1+0+0 / 5 = 0.6 (没算null)
答案没有
ASLf = 1+3+2+1+2 / 5 = 1.8(算null) -> C