查找
查找的基本概念
查找——在数据集合中寻找满足某种条件的数据元素的过程称为查找
查找表(查找结构)——用于查找的数据集合称为查找表,它由同一类型的数据元素(或记录)组成
关键字——数据元素中唯一标识该元素的某个数据项的值,使用基于关键字的查找,查找结果应该是唯一的。
静态查找表——只需进行操作(仅关注查找速度即可)
动态查找表——也要进行操作(除了查找速度,也要关注插/删操作是否方便实现)
查找长度——在查找运算中,需要对比关键字的次数称为查找长度
平均查找长度(ASL, Average Search Length)——所有查找过程中进行关键字的比较次数的平均值
评价一个查找算法的效率时,通常考虑查找成功/查找失败两种情况的ASL
顺序查找(很简单,不重点描述)
折半查找(仅适用于有序的顺序表)
算法思想及代码
①首先将给定值key与表中中间位置元素(a[mid])比较,若相等,则查找成功,返回该元素的存储位置;
②若不等,所需查找的元素只能在中间元素的前半部分或后半部分(mid左侧或右侧)
③然后在缩小的范围内继续进行①②操作,如此重复,直到找到为止,若查找不成功,返回查找失败的信息。
代码:
int binary_Search(int a[], int n, int key) {
int low = 0, high = n - 1, mid;
while (low <= high) {
mid = (low + high) / 2;
if (key == a[mid]) {
return mid;
} else if (key < a[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
查找效率分析
构造查找判定树(查找29为例)
分块查找(重点出选择题)
算法思想及代码
查找效率分析(ASL)
①
②
③
B树
B树的定义
B树,又称多路平衡查找树,B树中所有结点的孩子个数的最大值称为B树的阶,通常用m表示。一棵m阶B树或为空树,或为满足如下特性的m叉树:
- 树中每个结点⾄多有m棵⼦树,即⾄多含有m-1个关键字。
- 若根结点不是终端结点,则⾄少有两棵⼦树。
- 除根结点外的所有⾮叶结点⾄少有⌈m/2⌉棵⼦树,即⾄少含有⌈m/2⌉-1个关键字。
- 所有非叶结点的结构如下:
其中,Ki(i = 1, 2…, n)为结点的关键字,且满足K1<K2<…<Kn;Pi(i = 0,1…, n)为指向子树根结点的指针,且指针Pi-1所指子树中所有结点的关键字均小于Ki,Pi所指子树中所有结点的关键字均大于Ki,n(⌈m/2⌉-1≤n≤m -1)为结点中关键字的个数。 - 所有的叶结点都出现在同⼀层次上,并且不带信息(可以视为外部结点或类似于折半查找判定树的查找失败结点,实际上这些结点不存在,指向这些结点的指针为空)。
B树的高度
注:⼤部分学校算B树的⾼度不包括叶⼦结点(失败结点)
最小高度
最大高度(法一)
最大高度(法二)
B树的插入
5阶B树——结点关键字个数⌈m/2⌉ - 1 ≤ n ≤ m - 1
即:2≤n≤4(注:此处省略失败结点)
新元素一定是插入到最底层“终端节点”,用“查找”来确定插入位置
当一个分支插满时:
在插入key后,若导致原结点关键字数超过上限,则从中间位置(⌈m/2⌉)将其中的关键字分为两部分,左部分包含的关键字放在原结点中,右部分包含的关键字放到新结点中,中间位置(⌈m/2⌉)的结点插入原结点的父结点。
Example
若此时导致其⽗结点的关键字个数也超过了上限,则继续进⾏这种分裂操作,直⾄这个过程传到根结点为⽌,进
⽽导致B树⾼度增1。
B树的删除
5阶B树——结点关键字个数⌈m/2⌉ - 1 ≤ n ≤ m - 1
即:2≤n≤4(注:此处省略失败结点)
①若被删除关键字在终端节点,则直接删除该关键字(要注意节点关键字个数是否低于下限⌈m/2⌉ -1)
删除60
②若被删除关键字在非终端节点,则用直接前驱或直接后继来替代被删除的关键字
直接前驱︰当前关键字左侧指针所指子树中“最右下”的元素
删除80 (80用77替代)
直接后继:当前关键字右侧指针所指子树中“最左下”的元素
删除77(77用82替代)
对非终端结点关键字的删除,必然可以转化为对终端结点的删除操作
③删除后,节点关键字个数不符合要求则需要“借”
兄弟够借。若被删除关键字所在结点删除前的关键字个数低于下限,且与此结点右(或左)兄弟结点的关键字个数还很宽裕,则需要调整该结点、右(或左)兄弟结点及其双亲结点(父子换位法)
删除38
经父子换位法
1.
2.
最终结果
删除90
经父子换位法
1.
2.
④兄弟不够借。若被删除关键字所在结点删除前的关键字个数低于下限,且此时与该结点相邻的左、右兄弟结点的关键字个数均=⌈m/2⌉-1,则将关键字删除后与左(或右)兄弟结点及双亲结点中的关键字进行合并
删除49
需要合并
最终结果
B+树
B+树的概念
对比分块查找
一棵m阶的B+树需满足下列条件:
- 每个分支结点最多有m棵子树(孩子结点)
- 非叶根结点至少有两棵子树,其他每个分支结点至少有⌈m/2⌉棵子树(可以理解为:要追求“绝对平衡”,即所有子树高度要相同)
- 结点的子树个数与关键字个数相等
- 所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按大小顺序排列,并且相邻叶结点按大小顺序相互链接起来(支持顺序查找)
- 所有分支结点中仅包含它的各个子结点中关键字的最大值及指向其子结点的指针
B+树的查找
①查找成功与否只能通过叶子结点判断
②可多路查找也可顺序查找
B+树与B树的区别
- m阶B+树:结点中的n个关键字对应n棵⼦树
m阶B树:结点中的n个关键字对应n+1棵⼦树 - m阶B+树:根节点的关键字数n∈[1, m],其他结点的关键字数n∈[⌈m/2⌉, m]
m阶B树:根节点的关键字数n∈[1, m-1],其他结点的关键字数n∈[⌈m/2⌉-1, m-1] - m阶B+树:在B+树中,叶结点包含全部关键字,⾮叶结点中出现过的关键字也会出现在叶结点中
m阶B树: 在B树中,各结点中包含的关键字是不重复的 - m阶B+树:在B+树中,叶结点包含信息,所有⾮叶结点仅起索引作⽤,⾮叶结点中的每个索引项只含有对应⼦树的最⼤关键字和指向该⼦树的指针,不含有该关键字对应记录的存储地址。
m阶B树:B树的结点中都包含了关键字对应的记录的存储地址
典型应⽤:关系型数据库的“索引”(如MySQL)
在B+树中,⾮叶结点不含有该关键字对应记录的存储地址。
可以使⼀个磁盘块可以包含更多个关键字,使得B+树的阶更⼤,树⾼更矮,读磁盘次数更少,查找更快
散列查找
基本的散列查找
散列表(Hash Table),⼜称哈希表。是⼀种数据结构,特点是:数据元素的关键字与其存储地址直接相关若不同的关键字通过散列函数映射到同⼀个值,则称它们为“同义词”
通过散列函数确定的位置已经存放了其他元素,则称这种情况为“冲突”
处理冲突的方法
- “拉链法”处理冲突
最理想情况:散列查找时间复杂度可到达O(1)
查找失败的平均查找长度
- 开放定址法处理冲突
① 线性探测法 —— di = 0, 1, 2, 3, …, m-1;即发⽣冲突时,每次往后探测相邻的下⼀个单元是否为空
采⽤“开放定址法”时,删除结点不能简单地将被删结点的空间置为空,否则将截断在它之后填⼊散列表的同义词结点的查找路径,可以做⼀个“删除标记”,进⾏逻辑删除
查找效率分析(ASL)
总结:线性探测法很容易造成同义词、⾮同义词的“聚集(堆积)”现象,严重影响查找效率
产⽣原因——冲突后再探测⼀定是放在某个连续的位置
② 平⽅探测法 ——当di = 02, 12, -12, 22, -22, …, k2, -k2时,称为平⽅探测法,⼜称⼆次探测法其中k≤m/2(较好的解决线性探测法的剧集问题)
处理完后的结果为
⾮重点⼩坑:散列表⻓度m必须是⼀个可以表示成4j + 3的素数,才能探测到所有位置
③ 伪随机序列法——di 是⼀个伪随机序列,如 di= 0, 5, 24, 11, … - 再散列法——再散列法(再哈希法):除了原始的散列函数 H(key) 之外,多准备⼏个散列函数,
当散列函数冲突时,⽤下⼀个散列函数计算⼀个新地址,直到不冲突为⽌
常见的散列函数
- 除留余数法——H(key) = key % p
散列表表⻓为m,取⼀个不⼤于m但最接近或等于m的质数(质数⼜称素数。指除了1和此整数⾃
身外,不能被其他⾃然数整除的数)p
⽤质数取模,分布更均匀,冲突更少 - 直接定址法 —— H(key) = key 或 H(key) = a*key + b
其中,a和b是常数。这种⽅法计算最简单,且不会产⽣冲突。它适合关键字的分布基本连续的情
况,若关键字分布不连续,空位较多,则会造成存储空间的浪费
- 数字分析法 —— 选取数码分布较为均匀的若⼲位作为散列地址
设关键字是r进制数(如十进制数),而r个数码在各位上出现的频率不一定相同,可能在某些位上分布均匀一些,每种数码出现的机会均等﹔而在某些位上分布不均匀,只有某几种数码经常出现,此时可选取数码分布较为均匀的若干位作为散列地址。这种方法适合于已知的关键字集合,若更换了关键字,则需要重新构造新的散列函数。
- 平⽅取中法——取关键字的平⽅值的中间⼏位作为散列地址
具体取多少位要视实际情况⽽定。这种⽅法得到的散列地址与关键字的每位都有关系,因此使得
散列地址分布⽐较均匀,适⽤于关键字的每位取值都不够均匀或均⼩于散列地址所需的位数