查找
根据给定的值,在查找表中确定一个其关键字等于给定值的数据元素。
关键字
关键字又细分为主关键字和次关键字。若某个关键字可以唯一地识别一个数据元素时,称这个关键字为主关键字,例如学生的学号就具有唯一性;反之,像学生姓名、年龄这类的关键字,由于不具有唯一性,称为次关键字。
静态查找表和动态查找表
在查找表中只做查找操作,而不改动表中数据元素,称此类查找表为静态查找表;反之,在查找表中做查找操作的同时进行插入数据或者删除数据的操作,称此类表为动态查找表。
平均查找长度ASL
优点: 简单,对逻辑次序无要求,且不同储存结构均可使用。
缺点: ASL长,时间效率低
ASL: (1+2+..+n)/n=(n+1)/2
int Search_Seq(SSTable ST,KeyType key){
ST.R[0].key=key;//设置监视哨
for(i=S+T.length;ST.R[i].key!=key;---i);
return i;
}
前提:
只能是数组,不能是链表,并且数组有序
查找次数:
比较次数等于查找成功时位于的树的深度。
即 ⌊log2n⌋+1⌊log2n⌋+1
ASL:
log2(n+1)−1log2(n+1)−1 (n>50)
int search(int nums[], int size, int target) //nums是数组,size是数组的大小,target是需要查找的值
{
int left = 0;
int right = size - 1; // 定义了target在左闭右闭的区间内,[left, right]
while (left <= right) { //当left == right时,区间[left, right]仍然有效
int middle = left + ((right - left) / 2);//等同于 (left + right) / 2,防止溢出
if (nums[middle] > target) {
right = middle - 1; //target在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; //target在右区间,所以[middle + 1, right]
} else { //既不在左边,也不在右边,那就是找到答案了
return middle;
}
}
//没有找到目标值
return -1;
}
定义:查找表由"分块有序"的线性表和索引表组成。
思想:1、首先查找索引表 。索引表是有序表,可采用二分查找或顺序查找,以确定待查的结点在哪一块。2、然后在已确定的块中进行顺序查找,由于块内无序,只能用顺序查找。
静态树表为的是解决查找概率不等的记录。一般情况下,我们都是默认各个记录等概率查找的,但是有些记录可能不是等概率的。我们可能会首先搜索一些概率大的记录。
若在只考虑查找成功的情况下,描述查找过程的判定树其带权路径长度之和(用 PH 表示)最小时,查找性能最优,称该二叉树为静态最优查找树
总结:
顺序查找、二分(折半)查找和索引查找都是静态查找表,其中二分查找的效率最高。静态查找表的缺点是当表的插入或删除操作频繁时,为维护表的有序性,需要移动表中很多记录。
1.定义
2.查找过程
3.插入元素
4.删除元素
要删除二叉排序树中的p结点,分三种情况:
(1) p 为叶子结点:直接删除,再将父结点指针置空
(2) p 只有左子树或右子树
p只有左子树,用p的左孩子代替p
p只有右子树,用p的右孩子代替p
(3) p 左、右子树均非空:用左子树最大元素(前驱)或右子树最小元素(后继)来代替删除的结点 (这两个结点最多都只有1个子结点,因此可用上一种方案来删除这个被选择的结点)
5.缺点
6.性能:看构造形态
二叉排序树或是一棵空树,或是具有下列性质的二叉树:
左单旋
右单旋
先左旋后右旋
先右旋后左旋
先对以 B 为根结点的子树右单旋,再对以 A 为根结点的子树左单旋
AVL 树的插入、删除、查找均可在 O ( log n ) 时间内完成
平衡二叉树已经满足了我们对于查找性能的基本要求,但是,平衡二叉树的调整过程是非常费时费力的,因为左右子树高度差的保持条件是苛刻的,平衡二叉树的调整频率非常高,这样会导致总体的时间复杂度很高,所以,我们还是需要找到解决方案来进行改进,放宽这个左右子树高度差为1的苛刻条件。于是就引出本讲的重头戏,即红黑树。
除了平衡二叉树,红黑树是另一种自平衡的二叉树。放宽这个左右子树高度差为1的苛刻条件。
红黑树( 图解 + 秒懂 + 史上最全)_架构师-尼恩的博客-CSDN博客_红黑树
时间复杂度
参考:
【二叉树系列-05】BST、AVL和RBT三种树形结构的优劣比较 - 知乎
1.定义
一棵m阶B树,或为空树,或为满足下列特性对的m叉树。
6.所有的叶子节点都在同一层,子叶结点不包含任何信息。叶子结点处于同一层,可以用空指针表示,是查找失败到达的位置。
注:平衡m叉查找树是指每个关键字的左侧子树与右侧子树的高度差的绝对值不超过1的查找树,其结点结构与上面提到的B-树结点结构相同,由此可见,B-树是平衡m叉查找树,但限制更强,要求所有叶结点都在同一层。
2.查找
3.插入
与二叉排序树一样,B-树的创建过程也是将关键字逐个插入到树中的过程。
乙树的插入首先利用了B树的查找操作查找关键字ķ的插入位置。若该关键字存在于B树中,则不用插入直接返回,否则查找操作必定失败于某个最底层的非终端结点上,也就是要插入的位置。插入分两种情况讨论。
在B树中插入30,可以得到如下的结果:
再插入26,得到下图结果,插入后的终端结点中的关键字数大于m-1,需对结点进行分裂
详见参考:
B树详解__Kim的博客-CSDN博客_b树
4.删除
删除操作见上,链接
一棵m阶B+树要么是空树,要么满足以下定义:
(1)一个结点的结构是:(K1,P1,K2,…,Kn,Pn),其中P为指向子树的指针,K为关键字,它们一一对应
(2)对于根结点,如果它本身不是叶子结点,至少拥有 1 个关键字,即至少拥有 1 棵子树
(3)对于除根结点之外的所有结点,至少拥有⌈m/2⌉ 棵子树,即至少拥有 ⌈m/2⌉ 个关键字
(4)为了满足阶的定义,任何结点最多拥有 m 个子树,即最多拥有 m 个关键字
(5)B+树的所有数据保存在叶子结点,且叶子结点由指针链接起来
(6)所有叶子结点都处在同一层次上,每个叶子结点中关键字的个数均介于 ⌈ m / 2 ⌉ 和 m 之间
在上一节中,在 B-树中的每个结点关键字个数 n 的取值范围为⌈m/2⌉ -1≤n≤m-1
,而在 B+树中每个结点中关键字个数 n 的取值范围为:⌈m/2⌉≤n≤m
参考:
B+树及插入和删除操作详解
①节点结构区别:B+树内节点不存储数据,所有 data 存储在叶节点导致查询时间复杂度固定为 log n。而B-树节点存key和data,查询时间复杂度不固定,与 key 在树中的位置有关,最好为O(1)。
② 叶子节点:B+树叶节点两两相连可大大增加区间访问性,可使用在范围查询等,而B-树叶子节点是没有联系的,每个节点 key 和 data 在一起,则无法区间查找。
③B+树更适合外部存储。由于内节点无 data 域,每个节点能索引的范围更大更精确
1、检索效率更高:B树只适合随机检索,而B+树同时支持随机检索和顺序检索;
2、B+树空间利用率更高
因为B+树的内部节点(非叶子节点,也称索引节点)不存储数据,只存储索引值,相比较B树来说,B+树一个节点可存储更多的索引值,使得整颗B+树变得更矮,减少I/O次数,磁盘读写代价更低,I/O读写次数是影响索引检索效率的最大因素;
3、B+树查询效率更加稳定
因为在B+树中,顺序检索比较明显,随机检索时,由于B+树所有的 data 域(结点中存储数据元素的部分)都在根节点,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径相同,导致每一个关键字的查询效率基本相同,时间复杂度固定为 O(log n);而B树搜索有可能会在非叶子节点结束,约靠近根节点的记录查找时间越短,其性能等价于在关键字全集内做一次二分查找,查询时间复杂度不固定,与 key 在树中的位置有关,最好情况为O(1);
4、B+树范围查询性能更优
因为B+树的叶子节点使用了指针顺序(链表)从小到大地连接在一起,B+树叶节点两两相连可大大增加区间访问性,只要遍历叶子节点就可以实现整棵树的遍历,而B树的叶子节点是相互独立的,每个节点 key(索引)和 data 在一起,则无法查找区间;
【根据空间局部性原理:如果一个存储器的某个位置被访问,那么将它附近的位置也会被访问】
若访问节点 key为 50,则 key 为 55、60、62 的节点将来也可能被访问,可利用磁盘预读原理提前将这些数据读入内存,减少了磁盘 IO 的次数。当然B+树也能够很好的完成范围查询,比如同时也会查询 key 值在 50-70 之间的节点。
5、B+树增删文件(节点)时,效率更高
因为B+树的叶子节点包含了所有关键字,并以有序的链表结构存储。
哈希查找 / 散列查找
散列法存储的基本思想:
建立记录关键码字与其存储位置的对应关系,或者说,由关键码的值决定数据的存储地址。这样,不经过比较,一次存取就能得到所查元素的查找方法
优点:查找速度极快(O(1)),查找效率与元素个数n无关!
哈希函数和哈希表:
必须在记录的关键字和它的存储位置之间建立一个映射 f ,将 f 称为哈希函数
哈希函数只是一种映象,所以哈希函数的设定很灵活,只要使任何关键字的哈希函数值都落在哈希表长允许的范围之内即可
哈希表 (Hash Table) 即为 “关键字 → 地址” 的表
冲突 (collision):
k e y 1 ≠ k e y 2 但 H ( k e y 1 ) = H ( k e y 2 )
同义词:
哈希函数值相同的两个关键字。
冲突:
哈希函数通常是一种压缩映象,所以冲突不可避免,只能选择一个好的哈希函数,尽量减少产生冲突的机率;同时,冲突发生后,应该有处理冲突的方法。
哈希函数的标准:
选择一个好的哈希函数的标准:能将关键字均匀的分布在存储空间中。
选取哈希函数,考虑以下因素:
构造:取关键字或关键字的某个线性函数作哈希地址。
这种哈希函数简单,并且对于不同的关键字不会产生冲突,但可以看出这是一种较为特殊的哈希函数,实际生活中,关键字的元素很少是连续的。用该方法产生的哈希表会造成空间大量的浪费,因此这种方法适应性并不强.
构造:取关键字被 p pp 除后所得余数作哈希地址(p ≤ 哈希表表长m )
不仅可以对关键字直接取模,也可以在折叠、平方取中等运算后取模。
特点
以上三个方法参考:
查找 (二):动态查找表 (二叉排序树、平衡二叉树、B-树、B+树、散列查找)_连理o的博客-CSDN博客_动态查找表
参考:
数据结构笔记_技术交流_牛客网