王道 数据结构 第七章 查找 B树开始
一、B树
引入
如何保证查找效率?
若每个结点内关键字太少,导致树变高,要查找更多层结点,效率低
策略1:m叉查找树中,规定除了根结点外,任何结点至少有[m/2]个分叉,即至少含有[m/2]-1个关键字
[m/2]:向上取整
eg:对于5叉排序树,规定除了根结点外,任何结点至少有3个分叉,2个关键字
策略2:m叉查找树中,规定对于任何一个结点,其所有子树的高度都要相同
满足以上两个策略的树就称为B树
定义
B树,又称多路查找树,B树中的所有结点的孩子个数的最大值称为B树的阶,通常用m表示。一棵m阶B树或为空树,或为满足如下性质的m叉树:
- 1.树中每个结点至多有m棵子树,即最多有m-1个关键字
2.若根结点不是终端结点,则至少有两棵子树
3.除根节点外,所有的非叶结点至少有[m/2]棵子树,即至少有[m/2]-1个关键字
4.所有的叶结点都出现在同一层次上,且不带信息(可以视为外部结点或类似与折半查找判定树的查找失败结点,实际上这些结点不存在,指向这些结点的指针为空)
5.所有非叶结点的结构如下:
其中,Ki(i = 1,2,…,n)为结点的关键字,且满足K1<K2<…<Kn;Pi为指向子树根结点的指针,且指针Pi-1所指子树中所有结点的关键字均小于Ki,Pi所指所指子树中所有结点的关键字均大于Ki,n([m/2]-1≤n≤m-1)为结点中关键字的个数。
B树的高度
问题:含n个关键字的m阶B树,最小高度、最大高度是多少?
最小高度——让每个结点尽可能的满,有m-1个关键字,m个分叉,则有:
n ≤ (m-1)*(1+m+m ^ 2+……m ^ h-1)
最大高度——让各层分叉尽可能的少,即根结点只有2个分叉,其他结点只有[m/2]个分叉
各层结点至少有:第一层 1、第二层 2、第三层 2[m/2]……第h层 2([m/2])^(h-2)
第h+1层共有叶子结点(失败节点)2([m/2])^(h-1)
n个关键字的B树必有n+1个叶子结点,则n+1 ≥ 2([m/2])^(h-1),即h≤log[m/2] ((n+1)/2+1)
三、散列表
散列查找
装填因子 = 表中记录数/散列表长度
装填因子会直接影响散列表的查找效率
散列函数——让不同关键字的冲突尽可能少
除留余数法——H(key) = key % p
散列表长度为m,取一个不大于m但最接近或等于m的质数p
直接定址法——H(key) = key 或 H(key) = a*key+b
它适合关键字分布基本连续的情况,若关键字分布不连续,空位较多,则会造成存储空间浪费
数字分析法——选取数码分布较为均匀的若干位作为散列地址
设关键字是r进制数(如十进制数),而r个数码在各位上出现的频率不一定相同,可能在某些位上分布均匀一些,每种数码出现的机会均等,此时可选取数码分布较为均匀的若干位作为散列地址。这种方法适合于关键字已知的集合,若更换了关键字,则需要重新构造新的散列函数
平方取中法——去关键字的平方值的中间几位作为散列地址
具体取多少位要视情况而定,适用于关键字的每位取值都不够均匀或均小于散列地址所需的位数
处理冲突的方法——开放寻址法
线形探测法——即发生冲突时,每次往后探测相邻的下一个单元是否为空
可能与同义词、非同义词发生冲突
空地址的判断算作一次比较
关于关键字的删除操作
线形探测法越早遇到空位置,越早认定查找失败,提升效率
注意:采用“开放寻址法”时,删除结点不能简单地将被删结点的空间置为空,否则将截断在它之后填入散列表的同义词结点的查找路径,可以做一个“删除标记”,进行逻辑删除
平方探测法——di = 02,12,-12,22,-22,……,k,k2,又称二次探测法,其中k≤m/2
比起线性探测法,更不容易产生“堆积”问题
散列表长度m必须是一个可以表示成4j+3的素数,才能探测到所有位置
伪随机序列法——di是一个伪随机序列
再散列法——多准备几个散列函数
除了原始的散列函数H(key)之外,多准备几个散列函数,当发生冲突时,用下一个散列函数计算一个新地址,直到不冲突为止
处理冲突的方法——拉链法
优化:根据关键字大小对链进行排序,之后查找可使用二分方法
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)