二分查找总结

本文详细介绍了二分查找算法的定义、工作流程、性能分析以及适用场景。二分查找适用于有序数组,每次迭代都将查找区间缩小一半,时间复杂度为O(logn)。在数据量较大、数据静态且有序的场景下,二分查找具有高效性。但非顺序表结构、频繁插入删除及数据量过小或过大时不宜使用。文章还探讨了编写二分查找算法的要点,并给出了不同场景下的实现方式。
摘要由CSDN通过智能技术生成

 

一、概述

1、定义

二分查找是一种广泛使用的搜索算法,主要用于在有序数组(一般是升序,后面的内容也只是针对升序情况)上查找元素

2、主要思想

二分查找算法背后的主要思想是充分利用元素之间的有序性、以及数组的随机访问特性,每次都与查找区间的中间元素比较大小,根据比较结果不断将查找范围缩小一半(采用分治策略,剪枝?),直到元素被找到,或者,查找区间被缩小为0

3、工作流程

将中间元素与target进行比较

如果匹配,则返回true

如果不匹配,

       如果中间元素比target大,则继续搜索左半部分;

       如果中间元素比target小,则继续搜索右半部分

不断搜索,直到找到target或者查找区间变为0

4、伪代码

以下是二分查找的伪代码,当数组没有重复元素时,返回target的下标或者查找失败的标志;当数组有重复元素时,返回其中一个元素的下标,具体返回哪个取决于数组中数据的分布状况

5、性能分析

二分查找是一种高效的查找算法,当数据规模为n,由于每次迭代数据都会缩小为原来的一般,所以被查找区间的大小变化依次为:n, n/2, n/4, n/8, ..., n/2^k, ...。也就是按等比数列的形式缩小。由于当数据量大小为1时,停止迭代,所以,另n/2^k = 1,即可计算出总共迭代次数为k = logn。而时间复杂度无非就是循环次数,所以时间复杂度为O(logn)。

空间复杂度

比较次数   最坏最好

二、使用场景与使用要点

1、什么场景下可以使用二分查找?

一般用于查找数组元素,并且数组在查找之前必须已经排好序(一般是升序)

这里的关键词有两个,一个是数组,数组是顺序存储结构,具有随机访问特性,另一个是有序,如果是无序数组,要查找一个元素就只能通过顺序遍历了

也就是说,二分查找的使用场景是有局限性的

2、什么场景下不能使用二分查找?

(1)数据是非顺序表结构存储的,不能使用二分查找

二分查找依赖顺序表结构(数组)。不能使用链表,因为二分查找需要按照下标随机访问元素,而链表根据下标访问元素的时间复杂度是O(n),如果基于链表,那么二分查找算法的时间复杂度会变高

(2)如果一组数据是非静态的(需要频繁删除、插入),不能使用二分查找

在二分查找之前需要将数组排好序。因此,如果针对一个经常插入删除元素的数组使用二分查找,要么需要在每次插入删除时保证数据有序,要么在每次二分查找之前都先进行排序,而不管是哪种方式,维护有序的成本都是非常高的

(3)数据量太小,不适合二分查找

二分查找只有在数据量比较大的时候才有优势,如果数据量很小,用顺序遍历即可。

不过有个例外,如果数据之间的比较操作非常耗时,那么不管数据量大小,最好都使用二分查找。这是因为,二分查找的时间不仅仅取决于时间复杂度量级 log n(即,循环的次数),还受每次比较所花费时间的影响(每一轮循环,都需要进行一两次比较,在进行时间复杂度分析时我们往往忽略常数项,将二分查找的复杂度定义为O(logn),但是分析实际场景下的性能时也不能忽略掉它)

如果数组中存储的都是长度超过300的字符串,那么比较两个字符串的操作就比较耗时了,此时比较次数的减少能大大提高性能,因此,我们要尽可能减少比较次数,用二分查找比顺序遍历更有优势

(4)数据量太大,不适合二分查找

二分查找底层依赖数组这种数据结构,而数组为了支持随机访问的特性,要求内存空间连续,对内存的要求比较苛刻。1GB大小的数组用数组存的话就需要1GB的连续内存空间,即便剩余的内存空间远大于1GB但是内存空间都是零散的,那这块内存也不能使用

3、编写二分查找算法的要点?

总的来说,编写一个二分查找算法大致需要考虑下面的东西

(1)查找区间:查找区间可以是闭区间也可以是半开半闭区间,但是最好还是使用闭区间(虽然 STL 中的二分查找用的是左闭右开区间)。初始化闭区间一般用的是两个“游标”low和high指向区间头部元素和尾部元素。注意,不同需求下查找区间可能不同。

(2)循环条件:循环条件可以用left < right,也可以使用left <= right,但是要注意两种方式在处理边界上是有所区别的

(3)中间元素的下标:在循环内部,我们需要计算中间元素的下标,计算的方式也有两种,一种是mid := floor(low + high / 2),一种是mid := ceil(left + right / 2), 前者是向下取整,后者是向上取整,当区间元素个数为奇数时两者没有区别,当区间元素为偶数时,前者拿到的是中间两个元素的前一个,后者拿到的是中间元素的后一个。在含有重复元素的二分查找场景中,没有选择好中间元素下标的计算方式,很可能引发死循环。

int mid =  left + (right - left) / 2 或  int mid = left + (right - left) >> 1 是向下取整的方式;

int mid = right - (right - left) / 2 或 int right = right - (right - left) >> 1 是向上取整的方式

不要使用int mid = (left + right) / 2来计算mid,因为left + right之后可能会溢出。另外,尽量使用位运算,因为计算机处理位运算比处理除法运算要快得多。

(4)left 和 right的更新

通常用到的更新方式有left = mid + 1 和 right = mid - 1,此外还有left = mid 和 right = mid,但是,在使用后面两种的时候要留意边界条件(主要是只剩两个元素的时候),否则非常容易出现死循环。

三、二分查找的实现

1、数组没有重复元素,查找target

    /**
     *
     * @param nums
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值