【二分查找】

        二分查找针对的是一个有序的数据集合,查找思想有点类似分治思想。每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为 0。

        二分查找是一种非常高效的查找算法,时间复杂度是 O(logn)。O(logn) 这种对数时间复杂度,是一种极其高效的时间复杂度,有的时候甚至比时间复杂度是常量级O(1) 的算法还要高效。二分查找更适合处理静态数据,也就是没有频繁的数据插入、删除操作。

        使用循环和递归都可以实现二分查找。

        

二分查找应用场景的局限性:

  • 二分查找依赖的是顺序表结构,简单点说就是数组。(链表不可以)

  • 二分查找针对的是有序数据。(如果数据没有序,我们需要先排序。)

  • 数据量太大不适合二分查找。

 

四种常见的二分查找变形问题

1.查找第一个值等于给定值的元素

2.查找最后一个值等于给定值的元素

3.查找第一个大于等于给定值的元素

4.查找最后一个小于等于给定值的元素

 

适用性分析

1.凡事能用二分查找解决的,绝大部分我们更倾向于用散列表或者二叉查找树,即便二分查找在内存上更节省,但是毕竟内存如此紧缺的情况并不多。

2.求“值等于给定值”的二分查找确实不怎么用到,二分查找更适合用在”近似“查找问题上。比如上面讲几种变体。


【跳表】

        跳表是一种动态数据结构,可以支持快速的插入、删除、查找操作,写起来也不复杂,甚至可以替代红黑树(Red-black tree)。Redis 中的有序集合(Sorted Set)就是用跳表来实现的。

算法笔记05-查找、跳表、散列表

        链表加多级索引的结构,就是跳表。

        在一个单链表中查询某个数据的时间复杂度是 O(n)。那在一个具有多级索引的跳表中查询任意数据的时间复杂度是 O(logn)。这个查找的时间复杂度跟二分查找是一样的。换句话说,我们其实是基于单链表实现了二分查找。(这种查询效率的提升,前提是建立了很多级索引,也就是空间换时间的设计思路。)

        跳表的空间复杂度是O(n)。也就是说,如果将包含 n 个结点的单链表构造成跳表,我们需要额外再用接近 n 个结点的存储空间。

        在实际的软件开发中,原始链表中存储的有可能是很大的对象,而索引结点只需要存储关键值和几个指针,并不需要存储对象,所以当对象比索引结点大很多时,那索引占用的额外空间就可以忽略了。

        跳表这个动态数据结构,不仅支持查找操作,还支持动态的插入、删除操作,而且插入、删除操作的时间复杂度也是 O(logn)。

算法笔记05-查找、跳表、散列表

        作为一种动态数据结构,我们需要某种手段来维护索引与原始链表大小之间的平衡,也就是说,如果链表中结点多了,索引结点就相应地增加一些,避免复杂度退化,以及查找、插入、删除操作性能下降。

        跳表是通过随机函数来维护“平衡性”,当我们往跳表中插入数据的时候,我们可以选择同时将这个数据插入到部分索引层中。

算法笔记05-查找、跳表、散列表

为什么 Redis 要用跳表来实现有序集合,而不是红黑树?

Redis 中的有序集合支持的核心操作主要有下面这几个:

  • 插入一个数据;

  • 删除一个数据;

  • 查找一个数据;

  • 按照区间查找数据(比如查找值在 [100, 356] 之间的数据);

  • 迭代输出有序序列。

对于按照区间查找数据这个操作,跳表可以做到 O(logn) 的时间复杂度定位区间的起点,然后在原始链表中顺序往后遍历就可以了。这样做非常高效。


        散列表用的是数组支持按照下标随机访问数据的特性,所以散列表其实就是数组的一种扩展,由数组演化而来。可以说,如果没有数组,就没有散列表。

        散列函数,可以把它定义成hash(key),其中 key 表示元素的键值,hash(key) 的值表示经过散列函数计算得到的散列值。

 

散列函数设计的基本要求:

    1. 散列函数计算得到的散列值是一个非负整数;

    2. 如果 key1 = key2,那 hash(key1) == hash(key2);

    3. 如果 key1 ≠ key2,那 hash(key1) ≠ hash(key2)。

 

散列冲突

        再好的散列函数也无法避免散列冲突。常用的散列冲突解决方法有两类,开放寻址法(open addressing)和链表法(chaining)。

        开放寻址法的核心思想是,如果出现了散列冲突,我们就重新探测一个空闲位置,将其插入。三种探测方法是:线性探测(Linear Probing)、二次探测(Quadratic probing)、双重散列(Double hashing)。

 

相关文章: