前言
本章主要详解了查找的概念和评价指标以及算法,其中包括顺序查找、分块查找、B和B+树的概念以及相关操作(增删改查),最后介绍了散列查找的概念,引入拉链法用于处理和解决散列查找的位置冲突问题等内容。
文章目录
查找的概念:
查找:在数据集合中寻找满足某种条件的数据元素的过程称为查找。
查找表(查找结构):用于查找的数据集合称为查找表,它由同一类型的数据元素(或记录)组成。
关键字:数据元素中唯一标识该元素的某个数据项的值,使用基于关键字的查找,查找结果应该是唯一的。
查找算法的评价指标:
查找长度:在查找运算中,需要对比关键字的次数称为查找长度。
平均查找长度ASL(Average Search Length):所有查找过程中进行关键字的比较次数的平均值。
顺序查找:
正常查找:
typedef struct{ //查找表的数据结构(顺序表)
ElemType *elem; //动态数组基址
int TableLen; //表的长度
}SSTable;
//顺序查找
int Search_Seq(SSTable ST,ELemType key){
int i;
for(i=0; i<ST.TableLen && ST.elem[i]!=key; ++i);
//查找成功,则返回元素下标;查找失败,则返回-1
return i==ST.TableLen? -1: i;
}
顺序查找(哨兵):
typedef struct{ //查找表的数据结构(顺序表)
ElemType *elem; //动态数组基址
int TableLen; //表的长度
}SSTable;
//顺序查找
int Search_Seq(SSTable ST,ELemType key){
ST.elem[0]=key; //设置哨兵
int i;
for(i=ST.Tablelen; ST.elem[i]!=key; --i);//从后向前找
//查找成功,则返回元素下标;查找失败,则返回下标0
return i;
}
设置哨兵的优点:无需判断是否越界,效率更高。
折半查找:
typedef struct{ //查找表的数据结构(顺序表)
ElemType *elem; //动态数组基址
int TableLen; //表的长度
}SSTable;
//折半查找
int Binary_Search(SStable L,Elemtype key){
int low=0,high=L.Tablelen-1,mid;
while(low<=high){
mid=(low+high)/2;//取中间位置
if(L.elem[mid]==key)
return mid;//查找成功则返回所在位置
else if (L.elem[mid]>key)
high=mid-1;//从前半部分继续查找
else
low=mid+1;//从后半部分继续查找
}
return -1; //查找失败,返回-1
}
折半查找,又称“二分查找”,仅适用于有序的顺序表。
查找效率的分析:
折半查找的时间复杂度:0(log2(n+1))
分块查找:
分块查找,又称索引顺序查找,算法过程如下:
①在引表中确定待查记录所属的分块(可顺序、可折半)
②在块内顺序查找
B/B+树:
终端接点:树的最下面一层含有实际信息的结点称为终端接点。
叶子结点:查找失败的结点称为叶子结点。
如何保证查找效率:
如图:这种情况下,若每个结点内关键字太少,导致树变高,要查更多层结点,效率低。
解决的策略:m叉查找树中,规定除了根节点外,任何结点至少有[m/2]个分叉,即至少含有[m/2]-1个关键字。([m/2]表示m/2的上取整数)
如图:虽然满足了分支条件,但是树还是很高。
解决的策略: m叉查找树中,规定对于任何一个结点,其所有子树的高度都要相同。
B树的概念:
B树,又称多路平衡查找树, ==B树中所有结点的孩子个数的最大值称为B树的阶,通常用m表示。==一棵m阶B树或为空树,或为满足如下特性的m叉树:
1)树中每个结点至多有m棵子树,即至多含有m-1个关键字。
2)若根结点不是终端结点,则至少有两棵子树。
3)除根结点外的所有非叶结点至少有[m/2]棵子树,即至少含有[m/2]-1个关键字。
4)所有非叶结点的结构如下:
结点中关键字的个数 | 指向子树根结点的指针 | 结点的关键字 | |||
---|---|---|---|---|---|
n | P0 | K1 | P1 | … | Pn |
其中, Ki (i = 1,2., n)为结点的关键字,且满足K1 < K2 <…< K n; Pi (i = 0,1., )为指向子树根结点的指针,且指针Pi-1所指子树中所有结点的关键字均小于Ki, Pi所指子树中所有结点的关键字均大于Ki, n([m/2]-1≤n≤m-1)为结点中关键字的个数。
5)所有的叶结点都出现在同一层次上,并且不带信息(可以视为外部结点或类似于折半查找判定树的查找失败结点,实际上这些结点不存在,指向这些结点的指针为空)
m阶B树的核心特性:
1)根节点的子树数∈[2, m],关键字数∈[1, m-1]。其他结点的子树数∈[[m/2], m],关键字数∈[[m/2]-1, m-1]。
2)对任一结点,其所有子树高度都相同。
3)关键字的值:子树0<关键字1<子树1<关键字2<子树2… (类比二叉查找树左<中<右) 。
B树的高度:
通常在计算中,B树的高度不包括叶子结点(即失败结点)
。
问题:含n个关键字的m阶B树,最小高度、最大高度是多少?
最小高度:让每个结点尽可能的满,有m-1个关键字, m个分叉,则有:
(
m
−
1
)
(
1
+
m
+
m
2
+
.
.
.
+
m
h
−
1
)
⩾
n
⟹
h
⩾
log
m
(
n
+
1
)
\left( \mathbf{m}-1 \right) \left( 1+\mathbf{m}+\mathbf{m}^2+...+\mathbf{m}^{\mathbf{h}-1} \right) \geqslant \mathbf{n}\Longrightarrow \mathbf{h}\geqslant \log _{\mathbf{m}}\left( \mathbf{n}+1 \right)
(m−1)(1+m+m2+...+mh−1)⩾n⟹h⩾logm(n+1)
最大高度:让各层的分叉尽可能的少,即根节点只有2个分叉,其他结点只有[m/2]个分叉各层结点至少有:第一层1、第二层2、第三层2[m/2] …第h层
2
(
[
m
2
]
)
h
−
2
2\left( \left[ \frac{\mathbf{m}}{2} \right] \right) ^{\mathbf{h}-2}
2([2m])h−2、第h+1层共有叶子结点(失败结点)
2
(
[
m
2
]
)
h
−
1
2\left( \left[ \frac{\mathbf{m}}{2} \right] \right) ^{\mathbf{h}-1}
2([2m])h−1。
n个关键字的B树必有n+1个叶子结点, 则 n + 1 ⩾ 2 ( [ m 2 ] ) h − 1 \text{则}\mathbf{n}+1\geqslant 2\left( \left[ \frac{\mathbf{m}}{2} \right] \right) ^{\mathbf{h}-1} 则n+1⩾2([2m])h−1, h ⩽ log [ m 2 ] n + 1 2 + 1 \mathbf{h}\leqslant \log _{\left[ \frac{\mathbf{m}}{2} \right]}\frac{\mathbf{n}+1}{2}+1 h⩽log[2m]2n+1+1 。
B树的插入和删除:
B树的插入:
①对m阶B树–除根节点外,结点关键字个数[m/2]-1≤n≤m-1
②子树0<关键字1<子树1<关键字2<子树2<…
【注】:新元素一定是插入到最底层“终端节点”,用“查找”来确定插入位置。
在插入key后,若导致原结点关键字数超过上限,则从中间位置([m/2])将其中的关键字分为两部分,左部分包含的关键字放在原结点中,右部分包含的关键字放到新结点中,中间位置([m/2])的结点插入原结点的父结点。若此时导致其父结点的关键字个数也超过了上限,则继续进行这种分裂操作,直至这个过程传到根结点为止,进而导致B树高度增1。
B树的删除:
兄弟不够借。若被删除关键字所在结点删除前的关键字个数低于下限,且此时与该结点相邻的左、右兄弟结点的关键字个数均=[m/2]-1,则将关键字删除后与左(或右)兄弟结点及双亲结点中的关键字进行合并。
在合并过程中,双亲结点中的关键字个数会减1。若其双亲结点是根结点且关键字个数减少至0(根结点关键字个数为/时,有2棵子树) ,则直接将根结点删除,合并后的新结点成为根;若双亲结点不是根结点,且关键字个数减少到[m/2]-2,则又要与它自己的兄弟结点进行调整或合并操作,并重复上述步骤,直至符合B树的要求为止。
B+树的概念:
一棵m阶的B+树需满足下列条件:
1)每个分支结点最多有m棵子树(孩子结点)
2)非叶根结点至少有两棵子树,其他每个分支结点至少有[m/2]棵子树。
3)结点的子树个数与关键字个数相等。(与B树不同的地方)
4)所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按大小顺序排列,并且相邻叶结点按大小顺序相互链接起来。 (与B树不同的地方)
5)所有分支结点中仅包含它的各个子结点中关键字的最大值及指向其子结点的指针。(与B树不同的地方)
6)在B+树中,叶结点包含信息,所有非叶结点仅起索引作用,非叶结点中的每个索引项只含有对应子"树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。 (==在B树中,各结点中包含的关键字是不重复的。==这也是与B树不同的地方。)
散列查找:
散列表的概念:
散列表(Hash Table) ,又称哈希表
, 是一种数据结构,特点是:数据元素的关键字与其存储地址直接相关。
若不同的关键字通过散列函数映射到同一个值,则称它们为"同义词"。
通过散列函数确定的位置已经存放了其他元素,则称这种情况为“冲突”。
处理冲突的方法:拉链法
如何设计更好的散列表: