数据结构---查找

一、基本概念

查找

根据给定的值,在查找表中确定一个其关键字等于给定值的数据元素。

关键字

关键字又细分为主关键字和次关键字。若某个关键字可以唯一地识别一个数据元素时,称这个关键字为主关键字,例如学生的学号就具有唯一性;反之,像学生姓名、年龄这类的关键字,由于不具有唯一性,称为次关键字

静态查找表和动态查找表

在查找表中只做查找操作,而不改动表中数据元素,称此类查找表为静态查找表;反之,在查找表中做查找操作的同时进行插入数据或者删除数据的操作,称此类表为动态查找表

平均查找长度ASL

二、静态查找表

2.1 顺序表查找

优点: 简单,对逻辑次序无要求,且不同储存结构均可使用。

缺点: 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;
}

2.2 有序表查找

1、折半查找(二分查找)

前提:
只能是数组,不能是链表,并且数组有序
查找次数:
比较次数等于查找成功时位于的树的深度。
即 ⌊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;
}

2、 插值查找(二分查找改进)

3、斐波那契查找

2.3 索引顺序表的查找

定义:查找表由"分块有序"的线性表和索引表组成。

思想:1、首先查找索引表  。索引表是有序表,可采用二分查找或顺序查找,以确定待查的结点在哪一块。2、然后在已确定的块中进行顺序查找,由于块内无序,只能用顺序查找。

2.4 静态树表的查找

静态树表为的是解决查找概率不等的记录。一般情况下,我们都是默认各个记录等概率查找的,但是有些记录可能不是等概率的。我们可能会首先搜索一些概率大的记录。

若在只考虑查找成功的情况下,描述查找过程的判定树其带权路径长度之和(用 PH 表示)最小时,查找性能最优,称该二叉树为静态最优查找树

总结:

顺序查找、二分(折半)查找和索引查找都是静态查找表,其中二分查找的效率最高。静态查找表的缺点是当表的插入或删除操作频繁时,为维护表的有序性,需要移动表中很多记录。

三、动态查找表

3.1 二叉排序树(二叉搜索树)

1.定义

  • 二叉排序树或是一棵空树,或是具有下列性质的二叉树
    • 若它的左子树不空,则左子树上所有结点的值均小于等于它的结点的值
    • 若它的右子树不空,则右子树上所有结点的值均大于它的结点的值
    • 左、右子树本身又各是一棵二叉排序树
  • 因此,中序遍历二叉排序树可得到关键字的升序序列

2.查找过程

3.插入元素

4.删除元素

要删除二叉排序树中的p结点,分三种情况:
(1) p 为叶子结点:直接删除,再将父结点指针置空
(2) p 只有左子树或右子树
p只有左子树,用p的左孩子代替p
p只有右子树,用p的右孩子代替p
(3) p 左、右子树均非空:用左子树最大元素(前驱)或右子树最小元素(后继)来代替删除的结点 (这两个结点最多都只有1个子结点,因此可用上一种方案来删除这个被选择的结点)

5.缺点

  • 同一组关键字能够构造出多棵不同形态的二叉排序树,其平均查找长度的值各不相同,甚至可能差别很大

6.性能:看构造形态

3.2 平衡二叉树(AVL )

1.定义

二叉排序树或是一棵空树,或是具有下列性质的二叉树

  • 它的左、右子树深度之差的绝对值不大于1
  • 它的左、右子树都是平衡二叉树
  • 平衡因子 BF (Balance Factor):左子树的深度减去它的右子树的深度,则平衡二叉树上所有节点的平衡因子只可能为-1,0,1。

 2.插入

RR 型不平衡 (破坏者在被破坏者的右子树的右子树上)

左单旋

  • A 不一定是根结点,它是距离产生问题结点最近的且 ∣ B F ∣ > 1 的点。

LL 型不平衡 (破坏者在被破坏者的左子树的左子树上)

右单旋

在这里插入图片描述

LR 型不平衡

先左旋后右旋

  • 先对以 B 为根结点的子树左单旋,再对以 A 为根结点的子树右单旋

在这里插入图片描述

RL 型不平衡

先右旋后左旋

先对以 B 为根结点的子树右单旋,再对以 A 为根结点的子树左单旋
在这里插入图片描述

3.性能分析

AVL 树的插入、删除、查找均可在 O ( log ⁡ n ) 时间内完成

平衡二叉树已经满足了我们对于查找性能的基本要求,但是,平衡二叉树的调整过程是非常费时费力的,因为左右子树高度差的保持条件是苛刻的,平衡二叉树的调整频率非常高,这样会导致总体的时间复杂度很高,所以,我们还是需要找到解决方案来进行改进,放宽这个左右子树高度差为1的苛刻条件。于是就引出本讲的重头戏,即红黑树

3.3 红黑树(BRT)

1.定义

除了平衡二叉树,红黑树是另一种自平衡的二叉树。放宽这个左右子树高度差为1的苛刻条件。

  • 1.节点是红色或黑色。
  • 2.根节点是黑色。
  • 3.每个叶子节点都是黑色的空节点(NIL节点)。
  • 4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  • 5.从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点

2.操作

红黑树( 图解 + 秒懂 + 史上最全)_架构师-尼恩的博客-CSDN博客_红黑树

BST AVL BRT比较

时间复杂度

 参考:

【二叉树系列-05】BST、AVL和RBT三种树形结构的优劣比较 - 知乎

3.4B 树

1.定义

一棵m阶B树,或为空树,或为满足下列特性对的m叉树。

  1. 树中每个结点最多含有m棵子树
  2. 若根结点不是叶子结点,则至少有2个子树
  3. 除根结点之外的所有非终端结点至少有⌈m/2⌉棵子树
  4. 如果一个结点有n-1个关键字,则该结点有n个分支,且这n-1个关键字按照递增顺序排列
  5. 每个非终端结点中包含信息:(N,A0,K1,A1,K2,A2,...,KN)其中:
  • Ki(1≤i≤n)为关键字,且关键字按升序排序。
  • 指针Ai(0≤i≤n)指向子树的根结点,Ai-1指向子树中所有结点的关键字均小于Ki,且大于Ki-1;
  • 关键字的个数n必须满足:⌈m/2⌉-1≤n≤m-1。
  • 结点内关键字各不相等且按从小到大排列。
  • 结点结构如下:

6.所有的叶子节点都在同一层,子叶结点不包含任何信息。叶子结点处于同一层,可以用空指针表示,是查找失败到达的位置。

注:平衡m叉查找树是指每个关键字的左侧子树与右侧子树的高度差的绝对值不超过1的查找树,其结点结构与上面提到的B-树结点结构相同,由此可见,B-树是平衡m叉查找树,但限制更强,要求所有叶结点都在同一层。

2.查找

  1. 先让key与根结点中的关键字比较,如果key等于K [i](K []为结点内的关键字数组),则查找成功。
  2. 若key<K [1],则到A[0]所指示的子树中进行继续查找(对A[]为结点内的指针数组),这里要注意B-树中每个结点的内部结构。
  3. 若key> K [n]的,则到A [n]所指示的子树中继续查找。
  4. 若k [i] <key <k [i + 1],则沿着指针A[i]所指示的子树继续查找。
  5. 如果最后遇到空指针,则证明查找不成功。

3.插入

与二叉排序树一样,B-树的创建过程也是将关键字逐个插入到树中的过程。

乙树的插入首先利用了B树的查找操作查找关键字ķ的插入位置。若该关键字存在于B树中,则不用插入直接返回,否则查找操作必定失败于某个最底层的非终端结点上,也就是要插入的位置。插入分两种情况讨论。

  • 插入关键字后该结点的关键字数小于等于m - 1,插入操作结束。
  • 插入关键字后该结点的关键字数等于m,则应该进行分裂操作,分裂操作如下:
  1. 生成一个新结点,从中间位置把结点(不包括中间位置的关键字)分为两部分。
  2. 前半部分留在旧结点中,后半部分复制到新结点中。
  3. 中间位置的关键字连同新结点的存储位置插入到父结点中,如果插入后父结点的关键字个数也超过了m-1,则继续分裂。

在B树中插入30,可以得到如下的结果:

 再插入26,得到下图结果,插入后的终端结点中的关键字数大于m-1,需对结点进行分裂

详见参考:

B树详解__Kim的博客-CSDN博客_b树

4.删除

删除操作见上,链接

3.5 B+树

1.定义

一棵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

2.查找

3.删除

4.插入

参考:

B+树及插入和删除操作详解

3.6 B树和B+树区别

节点结构区别:B+树内节点不存储数据,所有 data 存储在叶节点导致查询时间复杂度固定为 log n。而B-树节点存key和data,查询时间复杂度不固定,与 key 在树中的位置有关,最好为O(1)。
叶子节点:B+树叶节点两两相连可大大增加区间访问性,可使用在范围查询等,而B-树叶子节点是没有联系的,每个节点 key 和 data 在一起,则无法区间查找。
③B+树更适合外部存储。由于内节点无 data 域,每个节点能索引的范围更大更精确

为什么使用B+树

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+树的叶子节点包含了所有关键字,并以有序的链表结构存储


四、哈希表

哈希查找 / 散列查找

1.概念

散列法存储的基本思想

建立记录关键码字与其存储位置的对应关系,或者说,由关键码的值决定数据的存储地址。这样,不经过比较,一次存取就能得到所查元素的查找方法
优点:查找速度极快(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 ) 
同义词:

哈希函数值相同的两个关键字。

冲突:
哈希函数通常是一种压缩映象,所以冲突不可避免,只能选择一个好的哈希函数,尽量减少产生冲突的机率;同时,冲突发生后,应该有处理冲突的方法。

哈希函数的标准:

选择一个好的哈希函数的标准:能将关键字均匀的分布在存储空间中。

2.哈希函数构造方法

选取哈希函数,考虑以下因素:

  • 计算哈希函数所需时间 (哈希函数不应太复杂)
  • 关键字长度
  • 哈希表长度(哈希地址范围)
  • 关键字分布情况
  • 记录的查找频率 (查找频率高的记录应尽量避免冲突)

2.1 直接定址法

构造:取关键字或关键字的某个线性函数作哈希地址。

这种哈希函数简单,并且对于不同的关键字不会产生冲突,但可以看出这是一种较为特殊的哈希函数,实际生活中,关键字的元素很少是连续的。用该方法产生的哈希表会造成空间大量的浪费,因此这种方法适应性并不强.

2.2 除留余数法

构造:取关键字被 p pp 除后所得余数作哈希地址(p ≤  哈希表表长m )

 不仅可以对关键字直接取模,也可以在折叠、平方取中等运算后取模。

特点

  • 简单、常用
  • p 的选取很重要; p 选的不好,容易发生冲突。p 一般取小于等于表长 m的最大素数

2.3 数字分析法

2.4 平方取中法

2.5 折叠法

以上三个方法参考:

查找 (二):动态查找表 (二叉排序树、平衡二叉树、B-树、B+树、散列查找)_连理o的博客-CSDN博客_动态查找表

3.冲突处理方法

  • 处理冲突:为产生冲突的地址寻找下一个空的哈希地址

3.1 开放定址法

3.2 再哈希法

3.3 链地址法

4.哈希查找过程

4.1 哈希查找分析

参考:

数据结构笔记_技术交流_牛客网

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值