数据结构 7-0 查找

查找的基本概念

在这里插入图片描述
在这里插入图片描述
基本概念这里并没有什么很难理解的知识。有几个比较重要的概念,首先是两类查找表,静态的直接就是已经生成好了的,在查找静态查找表时,没找到就是没找到,不会在涉及到插入之类的操作,而动态的则是一边查找一边插入,如果没有找到就插入。除此之外,平均查找长度是一个很重要而且很常考的内容,分为查找成功时的平均查找长度和查找失败时的平均查找长度,计算思路都是求和取平均,区别在于成功时是将每个成功查找元素比较的次数求和,而失败时是每个位置查找失败时的长度。

顺序查找和折半查找

这两种查找方法是查找中最基础的两类查找方式。

顺序查找说白了就是依次找,从线性表的一端开始,逐个检测关键字是否符合条件,符合条件就是找到了,否则就是没找到。代码实现方面,一般采用倒序的方式,并将0位置设置为要查找的值,这样子就可以省去每一次判断是否已经查找完,使代码更加简洁。

顺序查找时,每次查找失败时查找次数必然是N+1次,所以失败时的平均查找长度为N+1,查找成功时平均查找长度与概率有关,其值为概率乘以逆序的比较次数。

顺序查找的缺点是当 n 较大时,平均查找长度较大,效率低:优点是对数据元素的存储没有要求,顺序存储或链式存储皆可。

当顺序查找的表内部有序时,也可以进行优化,假设是从小到大排列,可以根据大小关系,提前结束查找。这种情况下,查找成功的平均查找长度不发生变化,但是查找失败的平均查找长度会变,在概率相等的情况下有下面的公式。
在这里插入图片描述
有序表的顺序查找跟后面的折半查找的思想是不一样的,且有序表的顺序查找中的线性表可以是链式存储结构。

折半查找就是俗称的二分查找,先将给定值 key 与表中中间位置的元素比较, 若相等, 则查找成功, 返回该元素的存储位置;若不等,则所需查找的元素只能在中间元素以外的前半部分或后半部分,本质上就是利用了中间值来减小比较的范围。正因为需要中间值必须是中值,所以折半查找只适合有序表。

在这里插入图片描述
折半查找没有什么难以理解的地方,主要是确定好mid,有些题目中会在mid这里挖坑,比如在中间过程中将mid从向上取整偷换为向下取整。

折半查找的过程可以绘制成一个判定树。树中每个圆形结点表示一个 记录, 结点中的值为该记录的关键字值;树中最下面的时结点都是方形的, 它表示查找不成功的 情况。 从判定树可以看出, 查找成功时的查找长度为从根结点到目的结点的路径上的结点数,而 查找不成功时的查找氏度为从根结点到对应失败结点的父结点的路径上的结点数;每个结点值均 大于其左子结点值,且均小于于其右子结点值。
在这里插入图片描述
从判定树不难理解,在求折半查找的平均查找长度时,实际上就是在统计树里面几个节点的高度。而且定值的比较次数最多不会超过树的高度。
因为折半查找需要方便地定位查找区域,所以它要求线性表必须具有随机存取的特性。 因此, 该查找法仅适合于顺序存储结构,不适合于链式存储结构, 且要求元素按关键字有序排列。

还有一种方法叫做分块查找,和名字一样,就是将存储有数据的数组进行分块,将分块后的信息存放在单独的一个数组中。它吸取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找。 将查找表分为若干子块。块内的元素可以无序,但块之间是有序的,第一个块中的最大关键字小于第二个块中的所有记录的关键字,第二个块中的最大关键字小于第三个块中的所有记录的关键字,以此类推。再建立一个索引表,索引表中的每个元素含有各块 的最大关键字和各块中的第一个元素的地址, 索引表按关键字有序排列。

查找的时候也是利用了这一点,先根据索引表,确定目标可能在哪一个块,再进入对应的块内去具体地查找元素。正因如此,分块查找的平均查找长度为索引查找和块内查找的平均长度之和。

B树与B+树

这一块内容第一遍没学过,完全是新内容。

最重要的是理解B树的定义,定义很长而且不好理解。主要是把握好关键字数目和子树数目的数值关系,根据下面的非叶节点结构图可以看出,关键字和子树指针是分开的,相当于每两个子树之间都有一个关键字用于区分两个子树里面数据的大小范围,所以关键字数目一定是比子树数目少1的,除了根节点之外的非叶节点必须要有m/2向上取整个子树,根节点最少可以只有两棵子树,所有非叶节点最多只能有m棵子树。这几个数量关系是后面最多最少问题的关键,根据要求的是最少还是最多,设计对应的子树再求节点数目。在涉及高度等问题时,不需要记课本上复杂的公式,直接根据要求画一个简图即可。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在 B 树上进行查找与二叉查找树很相似,只是每个结点都是多个关键字的有序表,在每个结 点上所做的不是两路分支决定, 而是根据该结点的子树所做的多路分支决定。 查找时要找的节点实际上不是存放在叶节点上的,而是存放在到达叶节点过程中的节点的关键字里面。换句话说如果一次查找到最后查找到了叶节点,那就说明没有找到目标元素。

对B树的插入和删除是这一部分的难点,主要是插入后的调整十分麻烦。插入时分为两步,首先找到合适的位置,然后插入,由于B树有节点的关键字数目限制,所以插入操作可能会破坏这个限制,此时就需要进行调整。这里称为节点的分裂。
在这里插入图片描述
简单来说就是取中间关键字拿到双亲节点上,利用中间关键字把原节点的关键字区分为两部分,分别连接在不同的子树指针上。如果双亲节点加入一个节点后也超出了限制,那就继续分裂节点只到不会再超出限制。

删除也是一个道理,为了防止删除后节点里面的关键字数目少于最小值,需要补其余节点的关键字到这个节点上,分为三种情况。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

B+树是B树的一种变形,一般不太会考到,主要还是记住概念之类的即可。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

散列表

散列表主要是通过散列函数,将关键字直接转换为存储地址,这样的优点就在于可以直接取到元素,而不需要在数据结构里面移动来找这个元素。需要注意,散列函数的定义域必须包含全部需要存储的关键字,而值域的范围则依赖于散列表的大小或地址范围。散列函数计算出来的地址应该能等概率、均匀地分布在整个地址空间中,从而减少冲突 的发生,而且散列函数要尽可能简单计算。

常用的散列函数如下,最常用的还是除留余数法:
在这里插入图片描述
通过散列函数的定义不难看出,冲突是不可避免的,设计再好的散列函数,也有可能让两个不等的数映射到同一个位置上,所以解决冲突就是散列表另一个重要的内容。
在这里插入图片描述
上面四种开放定值法的方式中,线性探测就是从冲突位置顺序向后移直到可以插入,平方探测则是在冲突位置左右交替找位置,再散列是用第二个指定的散列函数再找一遍位置。也可以选择用拉链法,也称连接法,就是将冲突位置换成链表,向链表后面顺序添加即可。

在这里插入图片描述
对同一组关键字,设定相同的散列函数,则不同的处现冲突的方法得到的散列表不同,它们 的平均查找长度也不同。虽然散列表在关键字与记录的存储位置之间建立了直接映像,但由于“冲突”的产生,使得散列表的查找过程仍然是一个给定值和关键字进行比较的过程。 因此,仍需要以平均查找长度作为衡量散列表的查找效率的度量。 散列表的查找效率取决于三个因素:散列函数、处理冲突的方法和装填因子,装填因子是指表中已经存入的记录数目除以表的长度,装填因子越大表示散列表里面越满,从而产生冲突的概率也就越大。

典型题

在这里插入图片描述
对于此类题,有两种做法: 一种方法是,画出查找过程中构成的判定树,让最小的分支高度对应于最少的比较次数,让最大的分支高度对应于最多的比较次数, 出现类似于长度为 15 的顺序表时,判定树刚好是一棵满树,此时最多比较次数与最少比较次数相等; 另一种方法是,直接用公式求出最小的分支高度和最大分支高度。利用此方法不难得出答案至少4次至多5次。

在这里插入图片描述
这就是前面提到的在查找过程中挖坑,这道题的关键在于判断是不是偷换了向上取整还是向下取整,BCD都是偷换了,只有A符合条件。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43849505/article/details/107886918