常用查找算法总结

查找是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)

查找表按照操作方式分为静态查找表动态查找表

静态查找表:只作查找操作的查找表。它的主要操作是:
(1)查询某个“特定的”元素是否在查找表中
(2)检索某个“特定的”数据元素和各种属性

动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者从查找表中删除已经存在的某个数据元素。它的主要操作是:
(1)查找时插入数据元素
(2)查找时删除数据元素

1 顺序查找

顺序查找是在一个已知无(或有序)序队列中找出与给定关键字相同的数的具体位置。原理是让关键字与队列中的数从最后一个开始逐个比较,直到找出与给定关键字相同的数为止,它的缺点是效率低下。

2 有序查找

前提:线性表中的记录必须是关键字有序(通常从小到大),线性表必须采用顺序存储。
主要有以下查找方法:
(1)二分查找(又叫折半查找)
用数组保存数据,保证有序。二分查找速度很快,但是仅限于查找。因为插入的时候要保证有序,所以要往后移动数据以便插入。查找复杂度O(logn),插入复杂度O(n)。

(2)插值查找
先考虑一个新问题,为什么一定要是折半,而不是折四分之一或者折更多呢?
打个比方,在英文字典里面查“apple”,你下意识翻开字典是翻前面的书页还是后面的书页呢?如果再让你查“zoo”,你又怎么查?很显然,这里你绝对不会是从中间开始查起,而是有一定目的的往前或往后翻。
同样的,比如要在取值范围1 ~ 10000 之间 100 个元素从小到大均匀分布的数组中查找5, 我们自然会考虑从数组下标较小的开始查找。
经过以上分析,折半查找这种查找方式,还是有改进空间的,并不一定是折半的!
mid = (low+high)/ 2, 即 mid = low + 1/2 * (high – low);
改进为下面的计算机方案(不知道具体过程):mid = low + (key – a[low]) / (a[high] – a[low]) * (high – low),也就是将上述的比例参数1/2改进了,根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。

分析:从时间复杂度上来看,它也是o(n),但是对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么差值查找未必是很合适的选择。

(3)斐波那契查找
斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、····,在数学上,斐波那契被递归方法如下定义:F(1)=1,F(2)=1,F(n)=f(n-1)+F(n-2) (n>=2)。该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)

对于斐波那契数列:1、1、2、3、5、8、13、21、34、55、89……(也可以从0开始),前后两个数字的比值随着数列的增加,越来越接近黄金比值0.618。比如这里的89,把它想象成整个有序表的元素个数,而89是由前面的两个斐波那契数34和55相加之后的和,也就是说把元素个数为89的有序表分成由前55个数据元素组成的前半段和由后34个数据元素组成的后半段,那么前半段元素个数和整个有序表长度的比值就接近黄金比值0.618,假如要查找的元素在前半段,那么继续按照斐波那契数列来看,55 = 34 + 21,所以继续把前半段分成前34个数据元素的前半段和后21个元素的后半段,继续查找,如此反复,直到查找成功或失败,这样就把斐波那契数列应用到查找算法中了。

3 线性索引查找

索引就是把一个关键字与它对应的记录相关联的过程,一个索引由若干个索引项构成,每个索引项至少应包含关键字和其对应的记录在存储器中的位置等信息。+

索引按照结构可以分为:线性索引、树形索引和多级索引。
这里我们只介绍线性索引的三种:稠密索引、分块索引和倒排索引。
稠密索引

稠密索引是指:在线性索引中,将数据集中的每个记录对应一个索引项。
索引项有序也就意味着我们在查找关键字时可以用到折半、插值、斐波那契等有序查找算法,这大大提高了效率。
但是如果数据集非常大,维护查找如此大的索引表,性能反而大大下降。

分块索引
分块有序,是把数据集的记录分为若干块,并且这些块需要满足两个条件:
块内无序,即每一块内的记录不需要有序,当然,你如果能够让块内有序对于查找来说更理想,不过这通常需要付出大量空间和时间的代价。
块间​​有序。只有块间有序才能带来查找效率。
分块索引结构分三个数据项:
最大关键码,它存储每一块中的最大关键字,这样的好处就是可以使得在它之后的下一块中的最小关键字也能比这一块最大的关键字要大。
存储了块内记录的个数,以便循环时使用。
用于指向块首数据元素的指针,便于开始对这一块中记录进行遍历。

倒排索引
现在有两篇英文文章:
1.Books and friends should be few but good.
2.A good book is a good friend.
假设我们忽略掉“books”、“friends”中的“s”以及“A”这样的大小写差异。
我们可以整理出这样一张表:

a 2
and 1
be 1
book 1,2
but 1
few 1
friend 1,2
good 1,2
is 2
should 1

倒排索引的索引项的通用结构是:
次关键码,例如上面的“英文单词”
记录号表,例如上面的“文章编号”。
其中记录号表存储具有相同次关键字的所有记录的记录号(可以是指向记录的指针或者是该记录的主关键字)。
这样的索引方法就是倒排索引(in-verted index)。
倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中每一项都包含一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引。

4 二叉排序树(二叉查找树)

二叉排序树(Binary sort tree),又称二叉查找树。它是一棵空树,或者具有以下性质:
(1)若它的左子树不空,则左子树上所有节点的值均小于它的根结点的值;
(2)若它的右子树不空,则右子树上所有节点的值均大于它的根结点的值;
(3)它的左、右子树也分别为二叉排序树。

二叉排序树是以链接的方式存储,保持了链接存储结构在执行插入或删除操作时不用移动个元素的优点,只要找到合适的插入和删除位置之后,仅需修改链接指针即可。
二叉排序树的查找是从根节点到查找的节点的路径,其比较次数取决于给定值的节点在二叉排序树的层数。
二叉树的查找性能取决于二叉排序的形状,然而,二叉排序树的形状是不确定的,因此就有了后面的平衡二叉树。

5 平衡二叉树(AVL树)

平衡二叉树(Balanced Binary Tree)又被称为AVL树,且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用算法有红黑树、AVL、Treap、伸展树等。

平衡二叉树构建的基本思想是在构建二叉排序树的过程中,每当插入一个结点,先检查是否因插入破坏了树的平衡性,若是,则找出最小不平衡子树。在保持二拆排序树的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。

6 多路查找树

一个结点只能存储一个元素,在元素非常多的时候,就使得要么树的度非常大,要么树的高度非常大,甚至两者都必须足够大才行。
这就使得内存存取外存次数非常多,这显然成了时间效率上的瓶颈,这迫使我们打破每个结点只存储一个元素的限制。
多路查找树(muitl-way search tree),其每个结点的孩子数可以多于两个,且每个结点可以存储多个元素。(由于它是查找树,所以元素之间存在某种特定的排序关系)。

常用形式有:2-3树、2-3-4树、B树、B+树。

2-3树

2-3树是这样的一棵多路查找树:其中的每个结点都具有两个孩子(我们称它为2结点)或三个孩子(我们称它为3结点)。
一个2结点包含一个元素和两个孩子(或没有孩子),且与二叉排序树类似,左子树包含的元素小于该元素,右子树包含的元素大于该元素。不过,与二叉排序树不同的是,这个2结点要么没有孩子,要么就有两个,不能只有一个孩子。
一个3结点包含一小一大两个元素和三个孩子(或没有孩子),一个3结点要么没有孩子,要么具有三个孩子。如果某个3结点有孩子的话,左子树包含小于较小元素的元素,右子树包含大于较大元素的元素,中间子树包含介于两元素之间的元素。
并且2-3树中所有叶子结点都在同一层次上。
2-3树的插入删除操作只要保证2-3树严格保持2-3树的规则即可。

2-3-4树
2-3-4树其实就是2-3树的概念扩展,包括了4结点的使用。
一个4结点包含小中大单个元素和四个孩子(或没有孩子),一个4结点要么没有孩子要么具有4个孩子。如果某个4结点有孩子的话,左子树包含小于最小元素的元素;第二子树包含大于最小元素,小于第二元素的元素;第三子树包含大于第二元素,小于最大元素的元素;右子树包含大于最大元素的元素。

B树
B树(B-tree)是一种平衡的多路查找树,2-3树和2-3-4树都是B树的特例。结点最大的孩子数目称为B树的阶(order),因此2-3树是3阶B树,2-3-4树是4阶B树。

一个m阶的B树具有如下属性:
(1)如果根结点不是叶节点,则其至少有两棵子树。
(2)每一个非根的分支结点都有k-1个元素和k个孩子,其中[m/2]<=k<=m。每一个叶子结点n都有k-1个元素,其中[m/2]<=k<=m。
(3)所有叶子结点都位于同一层次。
(4)所有分支结点都包含n,A0,k1,A1,k2….

B树的典型应用:
要处理的硬盘数据很大,因此无法一次全部装入内存。因此我们会对B树进行调整,使得B树的阶数(或结点的元素)与硬盘存储的页面大小相匹配。
比如一个B数的阶数为1001(即1个结点包含1000个关键字),高度为2,它可以存储操作10亿个关键字,我们只要让根结点持久的保留在内存中,那么在这棵树上,寻找某一个关键字至多需要两次硬盘的读取即可。

B+树
为了解决所有元素的遍历等基本问题,我们在原有的B树结构基础上,加上了新的元素组织方式,这就是B+树。
一棵m阶的B+树和B树的差异在于:有n棵子树的结点中包含有n个关键字

所有的叶子结点包含全部关键字的信息,及指向含这些关键字记录的指针,叶子结点本身依关键字的大小自小而大顺序链接;
所有分支结点可以看成是索引,结点中仅含有其子树中的最大(或最小)关键字。

7 散列表查找(哈希表)

在进行查找时,在记录的存储位置与它的关键字之间建立一个确定的对应关系h,以线性表中每个元素的关键字K为自变量,通过函数h(K)计算出该元素的存储位置,我们将h函数称为散列函数或哈希函数。这种查找方法称为散列查找。

参考:
《大话数据结构》
http://blog.csdn.net/cdu09/article/details/23122623
https://noah_1992.gitbooks.io/-/content/xian_xing_suo_yin_cha_zhao.html

    原文作者:查找算法
    原文地址: https://blog.csdn.net/hejun_haitao/article/details/52663040
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞