1

什么是查找

查找可以定义如下

在一群元素中,找到这样一个元素,它满足给定的条件,这群元素具有相同的结构,其组织形式可以多种多样,比如,我们常见的数组,链表,树,图等等

而查找算法的目的就是要尽可能快的找到满足给定条件的元素,设想有一种方法,能让你直接拿到该元素,那么该算法的时间复杂度,就是O(1). 当然,这是最理想的情况,现实中我们要根据不同的数据结构,使用不同的方法,来不断的接近理想值。

那么,所谓的查找算法,为什么有的快,有的慢呢?你可以这样去理解。其实所有的查找算法都在做一件事,那就是排除

每一次查找操作,其实都是在做排除,如果说有一种方法能以O(1)的复杂度,找到元素,那么其实说明,它可以一下子排除候选集中的所有不相关元素,当然,这种算法,还是最理想化的。

不同查找算法的优劣其实也就是看它能不能一次排除更多的元素,比如暴力穷举法,这种方式就是一次只能排除一个元素,显然是最慢的,如果一次能排除一半的元素,那么这就是二分,如果每一次都能排除一大半的元素呢?

所以学习查找算法时,要考虑这个算法,一次能排除多少元素,那么排除的越多,就是越好的。

因为讲查找都是基于某一数据结构,所以本篇文章介绍各个数据结构下的查找算法,讲解每个算法时,关注的是它排除了多少元素。(其实也就是时间复杂度了)

一 数组

无序数组

1. 顺序查找

这个就是暴力穷举了,每次只能排除一个,不提,时间复杂度是0(n)。

有序数组

有序数组当然是有序了,不过,它有一个特点,就是它的每一个元素相当于把整个候选集分成了两部分,每次可以排除一部分。这就比暴力穷举好多了。

1. 二分查找

这个大家比较熟悉了,每次可以排除一半,时间复杂度是O(log2N)

其实二分查找是一种盲目的猜,猜的就是中间元素就是要找的元素。那么,我们能不能猜的更准一点呢?其实中有的。

2. 插值查找

为什么叫插值,其实我也说不清楚,管它呢。。。

现在想象一下如下这个数组

[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]

这个数组也是有序的,但有一个特点,每个元素相差一样,说明它们之间是均匀分布的,那么让你来查找一个75,你会怎么办?

现在闭眼想象一下,元素都是均匀分布的,所以,你知道,50肯定在中间,90肯定接近末尾,20肯定离开头比较近。那么,我们可以每次查找时,找到最接近的那个数字,而它的位置大概就在

要找的位置 = (距离开头的距离 / 总的距离)* 真实的距离

所以,我们每次比较的位置就是通过以上公式来计算的,这样,每次就不像2分查找那样,傻傻的只排除一半了。

适用情况:均匀分布

3. Fibonacci 查找

其实也是一种划分数组,进行排除的方式,据说性能优于二分查找,但好像看不懂证明,所以,知道就好,用到再说,因为你不知道什么情况下优于二分查找

数据查找的参考

https://www.cnblogs.com/penghuwan/p/8021809.html#_label2

二 树

这节介绍数下的查找方法,树的排除和数组的排除类似,数组是排除一部分,树更形象一点,排除一个分支。又另一种方式来说,就是剪枝。

1 无序树(不限于二叉树)

这种没办法,使用深度或广度方式吧。

2 二叉有序树

这种类似二分查找,每次排除一个分支

3 2-3 查找树

这种树长这样。

2-3查找树

其实就是有两种结点,2node和3node,和二叉树长的大差不差。查找方式也几乎雷同,只是构建这颗树比较有难度。。。

4 红黑树

红黑树

红黑树就是树2-3树变成了二叉树,2-3树中的3结点变成红色链接,同样的,这个结构的难度在于建立这个树,查找方法和二叉查找树一样。

5 再谈,二叉查找树,2-3树,红黑树

以上说了,二叉查找树,2-3树,红黑树,整了这么多,最好也是O(log2N)的效率,而且2-3,红黑,似乎还要比二叉查找树复杂啊?

其实2-3树,红黑树都有一个目的,就是要尽量接近平衡二叉树,只有接近了平衡二叉树,才能有O(log2N)的。

这里我们其实还忽略了另一个方面,就是这个结构并不仅仅用来查找,在实际项目中,还要增加,删除结构中的元素,对于二叉查找树,随着增加,删除的增多,慢慢的变的不平衡了,不平衡了,查找效率就下来了,而为了维持平衡,要付出巨大的插入,删除代价,这显然是不可接受的(因为我们的实际业务不仅仅是要求查找效率高)

而2-3树,红黑树就在插入和删除时,能尽量维持平衡,这也就在插入,删除,查找之间找到了一种平衡点。

6 B 树

B树是2-3树的更一般化,它也是一种平衡树,结点也是有序的,每个结点可以有多个值,所以,很好理解嘛,它长这样,它的查找算法,你也大概能猜到吧

Btree

7 B+ 树

innodb_primary_index

B+树和B树类似,关于B树,B+树的应用,可以参考[数据库基础概念]中关于索引的讨论一文

三 散列表

我们知道元素是在计算机中存储的,那么每个元素,就有一个地址,找到了这个地址,想当于找到了这个元素。

其实无论是数组也好,树也好,我们拿一个元素来检查是不是要找的值,实际上是先通过地址来得到元素的。所以说,地址很关键。

最简单的地址就是数组的Index了。

那么说,能不能直接定位到要查找元素的地址呢?假如我们有一个函数,可以根据元素的值,来计算出它应该出现的地址。是不是就不能一个个去比较了呢?就像下面这个公式

address = f(x) x为要查找的元素

而散列表的思想就是来自于此。以上函数就是散列表中最关键的hash函数。

散列表一般长这样

hash_index

一般散列表的结构都是有一个数组+链表来组成的。理想情况下,可以根据要查找的元素,直接计算出地址。但那是理想情况下。

散列表的关键设计就是散列函数了,这个函数要让值均匀分布才好

四 另话

其实,以上讨论的都是在一个数据集上的查找操作。

在实际应用中,我们的数据集上还上增加和删除操作。

有的算法和数据结构对查找有好,但是对增加和删除不友好。

所以我们要根据自己的数据集的特点,看查找,增加,删除,更新这些操作在该数据集上的频率等等,再选择是否需要使用这个数据结构。

当然还要考虑到实际计算机中不同存储体系的性能差距,如B树,B+树在数据库中的应用就体现了这一点。


asanelder
15 声望1 粉丝