1.1、原理分析

二分查找是一种非常简单易懂的快速查找算法,其思想在生活中随处可见,比如朋友聚会的时候爱玩的一个猜数游戏,我随机写一个0-100之间的数字,然后大家依次来猜,猜的过程中大家每猜一次我都会告诉大家猜大了还是猜小了,直到有人猜中为止,猜中的人会有一些惩罚措施。 这个过程其实就是二分查找思想的一种体现。

回到实际的开发场景中,假设有10个订单,其金额分别是:6,12,15,19,24,26,29,35,46,67。请从中找出订单金额为15的订单,利用二分查找的思想我们每次都与区间中间的数据进行大小的比较以缩小查找的范围,

下面这幅图代表了查找的过程,其中 left,right代表了待查找区间的下标,mid表示待查找区间中间元素的下标(如果范围区间是偶数个导致中间数有两个就选择较小的那个)

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

1.2、复杂度分析

理解了二分查找的思想后我们来分析二分查找的时间复杂度,首先我们要明确二分查找是一种非常高效的查找算法,通过分析其时间复杂度我们就可以发现,我们假设数据大小为n,每次查找完后数据的大小缩减为原来的一半,直到最后数据大小被缩减为1此时停止,如果我们用数据来描述其变化的规律那就是:

n,n/2,n/4,n/8,n/16,n/32,.......................,1;

可以看出来,这是一个等比数列,当数据大小变为1时:

其中k 的值就是总共缩小的次数。而每一次缩小操作只涉及两个数据的大小比较,所以,经过了 k 次区间缩小操作,通过计算k的值我们可以得出二分查找的时间复杂度就是 O(logn)

这是一种非常高效的时间复杂度,有时候甚至比O(1)复杂度更高效,为什么这么说呢?因为对于log n来说即使n非常的大对应的log n的值也会很小,之前在学习O(1)复杂度时我们讲过O(1)代表的是一种常量级复杂度并不是说代码只需要执行一次,有时候可能要执行100次,1000次这种常数级次数的复杂度都可以用O(1)表示,所以,常量级时间复杂度的算法有时候可能还没有 O(logn) 的算法执行效率高。

1.3、代码实现

二分查找的实现方式有基于循环的实现方式,也有基于递归的方式,现给出这两种方式编写的代码模板

1、基于循环的二分查找代码模板

// 返回的是元素在数组中的下标
public int binarySearch(int[] array, int target) {int left = 0, right = array.length - 1, mid;while (left <= right) {// mid = (left + right)>> 1mid  = left + ((right - left) >>1) ;if (array[mid] == target) {return mid;} else if (array[mid] > target) {right = mid - 1;} else {left = mid + 1;}}return -1;}
复制代码

用mid不断去逼近我们的目标值,相对好的情况直接在某段中间找到了目标值,最坏的情况是不断去逼近,最后left==right找到目标值,当然如果真的找不到目标值也就是left>right的时候。

2、基于递归的二分查找代码模板

public int recurBinarySearch(int[] array, int target,int left,int right) {//terminalif (left > right) {return -1;}//process current   计算中间元素的下标int mid = left + ((right - left)>>1);if (array[mid] == target) {return mid;}else if (array[mid] > target) {//drill downreturn recurBinarySearch(array,target,left,mid-1);}else {return recurBinarySearch(array,target,mid+1,right);}}
复制代码

进阶:二分查找的实现我们可以分为两大类情况

1,有序数列中不存在重复元素的简单实现;

2:有序数列中存在重复元素的变形实现,

针对第一种,上面已经给出了代码模板,针对第二种,在实际的应用场景中可能会出现如下几种情况:

2.1、从数据序列中查找第一个值等于给定值的元素,比如在{6,12,15,19,24,26,29,29,29,67}中找第一个等于29的元素

2.2、从数据序列中查找最后一个值等于给定值的元素。还是刚刚的元素序列,找最后一个等于29的元素

2.3、从数据序列中查找第一个大于等于给定值的元素。

2.4、从数据序列中查找出最后一个值小于等于给定值的元素。

课后思考:针对这四种情况,代码应该如何实现呢?

1.4、应用场景说明

二分查找的时间复杂度是O(log n),其效率非常高,那是不是说所有情况下都可以使用二分查找呢?下面我们讨论一下二分查找的应用前提

1、待查找的数据序列必须有序

二分查找对这一要求比较苛刻,待查找的数据序列必须是有序的(单调递增或者单调递减),假如数据无序,那我们要先排序,然后二分查找,如果我们针对的是一组固定的静态数据,也就说该数据序列不会进行插入和删除操作,那我们完全可以先排序然后二分查找,这样子一次排序多次查找;但是如果数据序列本身不是固定的静态的,可能涉及数据序列的插入和删除操作,那我们每次查找前都需要进行排序然后才能查找,这样子成本非常的高。

2、数据的存储依赖数组

待查找的数据序列需要使用数组进存储,也就是说依赖顺序存储结构。那难道不能用其他的结构来存储待查找的数据序列吗?比如使用链表来存储,答案是不可以的,通过我们前面实现的二分查找的过程可知,二分查找,算法需要根据下标,left,right,mid来访问数据序列中的元素,数组按照下标访问元素的复杂度是O(1),而链表访问元素的时间复杂度是O(n),因此如果使用链表来存储数据二分查找的时间复杂度就会变得很高。

3、数据序列存在上下边界

数据序列有上下边界,才能找到中间点,这样才能二分下去。

4、数据量太小或太大都不适合用二分查找

数据量很小的情况下,没有必要使用二分查找,使用循环遍历就够了,因为只有在数据量比较大的情况下二分查找才能体现出优势,不过在某些情况下即使数据量很小也建议大家使用二分查找,比如数据序列中的数据都是一些长度非常长的字符串,这些长度非常长的字符串比较起来也会非常的耗时,所以我们要尽可能的减少比较的次数,这样反倒有助于提高性能。

那为什么数据量太大的情况下也不建议使用二分查找呢?因为我们前面刚讲到二分查找底层需要依赖数组存储待查找的数据序列,而数组的特点是需要连续的内存空间,比如现在有1G的订单数据,如果用数组来存储就需要1G的连续内存,即便有2G的剩余内存空间,如果这2G的内存空间不连续那也无法申请到1G大小的数组空间,所以我们说数据量太大也不适合用二分查找。

2、面试实战

69. x 的平方根

字节跳动,美团点评最近面试题,69. x 的平方根

class Solution {// 求sqrt(x)即求:x=n^2 (n>0),就是我们所熟知的抛物线(y=x^2)右侧,单调递增,且有上下界,[1,x/2]public int mySqrt(int x) {//特殊判断if (x <=1) {return x;}//找一个数k,k^2<=x,找一个最大的k就是我们想要的long left=1,right = x>>1,mid = 0;while (left <=right ) {mid = (left+right) >>1;if (mid * mid  < x) {left = mid + 1;}else if (mid * mid > x) {right = mid - 1;}else {return (int)mid;}}return (int)left-1;}
}
复制代码

代码解释:

1:如果正好找到一个mid^2 = x则在while loop中就可以直接返回了,

2:如果while loop中还没找到,就类似x=8,我们在[ 1,2,3,4 ]中去寻找,while loop中最后一次循环left == right == 3,我们只需找k^2 <=x的最大k值即可。

进阶:牛顿迭代法解决该问题!参考精选题解:leetcode-cn.com/problems/sq…

同类题目:367. 有效的完全平方数

class Solution {public boolean isPerfectSquare(int x) {//特殊判断if (x <=1) {return true;}long left=1,right = x>>1,mid = 0;while (left <=right ) {mid = (left+right) >>1;if (mid * mid  < x) {left = mid + 1;}else if (mid * mid > x) {right = mid - 1;}else {return true;}}return false;}
}
复制代码

33. 搜索旋转排序数组

字节,快手,百度最近面试题,33. 搜索旋转排序数组

二分查找的变形题目

思考要点:

1:二分的条件:满足有上下边界,数组存储可利用下标获取,单调性这块原始数组是单调递增的,旋转之后虽然整体不是单调性的,但是其中有一半一定是单调递增的。

2:要达到log n的复杂度肯定是要二分,但是并不能简单套用二分的模板,我们需要先找到哪半部分是具备单调性的,并判断target是否在该范围内,如果在则在这部分查找target,如果不在则在另外半部分查找。

class Solution {public int search(int[] nums, int target) {//此处left,right代表的是下标int left=0,right = nums.length-1,mid=0;while (left <= right) {mid = (left+right) >>1;if (nums[mid] == target) {return mid;}//前半部分有序if (nums[left] <= nums[mid]) {//target如果在前半部分则在前半部分找,否则在后半部分找if (target < nums[mid] && target >=nums[left] ) {right = mid -1;}else {left = mid + 1;}}else {//后半部分有序//target如果在后半部分则在后半部分找,否则在前半部分找if (target > nums[mid] && target <= nums[right]) {left = mid +1;}else {right = mid -1;}}}return -1;}
}
复制代码

153. 寻找旋转排序数组中的最小值

网易,拼多多,今日头条面试题,153. 寻找旋转排序数组中的最小值

class Solution {//二分,但不能套用简单的二分模板,修改二分的判断条件public int findMin(int[] nums) {int left=0,right=nums.length-1,mid = 0;/*在这里如果left==right则可以直接返回了,最小元素一定是它*/while (left < right ) {mid = (left + right) >>1;if (nums[mid] < nums[right]) {//右区间连续,最小值一定在左半区间//mid可能是最小值也可能不是,简单二分的模板代码写right=mid-1;right = mid;}else if (nums[mid] > nums[right]) { //右边区间不连续,最小值一定在该区间内left = mid +1;}}return nums[left];}
}
复制代码

进阶思考题:

使用二分查找,寻找一个半有序数组 [4, 5, 6, 7, 0, 1, 2] 中间无序的地方

74. 搜索二维矩阵

新浪,爱奇艺,三星面试题,74. 搜索二维矩阵

解题思路:标准的二分m x n的矩阵可以看成 m x n 的有序数组

参考官方题解:leetcode-cn.com/problems/se…

class Solution {//标准的二分m x n的矩阵可以看成 m x n 的有序数组public boolean searchMatrix(int[][] matrix, int target) {int m = matrix.length;if (m ==0) {return false;}int n = matrix[0].length;int left = 0;int right = m * n  -1;int mid=0,row=0,col=0;while (left<=right) {mid = (left+right)>>1;//最重要的就是将mid转换陈二维数组中的下标。row = mid / n;col = mid % n;if (matrix[row][col] == target) {return true;}else if (matrix[row][col] < target) {left = mid +1;}else {right = mid-1;}}return false;}
}
复制代码

本文由传智教育博学谷教研团队发布。

如果本文对您有帮助,欢迎关注点赞;如果您有任何建议也可留言评论私信,您的支持是我坚持创作的动力。

发现了二分查找的秘密相关推荐

  1. 面试前必知必会的二分查找及其变种

    今天给大家带来的是二分查找及其变种的总结,大家一定要看到最后呀,非常非常用心的一篇文章,废话不多说,让导演帮我们把镜头切到袁记菜馆吧! 袁记菜馆内.... 店小二:掌柜的,您进货回来了呀,哟!今天您买 ...

  2. 二分查找式的debug

    为什么80%的码农都做不了架构师?>>>    是的,身边充斥着简单有用的方法和思想能够让解决问题的能力更加强大. 而且我们也知道这些,如果问的话甚至可以说出其中的关键细节. 但是我 ...

  3. 漫画:什么是二分查找

    ----- 第二天 ----- 什么意思呢?我们来举两个栗子: 给定一个有序数组 2,5,7,9,12,14,20,26,30 Case 1: Case 2: ------------ 为什么说这样效 ...

  4. java 二分查找_计算机入门必备算法——二分查找法

    1.引言 笔者对于计算机的研究一直停滞不前,近期想对一些算法进行复习和进一步的研究,每天都会更新一个新的算法,算法有难有易,层层递进.不希望能学的有多么高深,只希望在一些最基本的算法上有编码的思路,或 ...

  5. 你真的会二分查找吗?

    引用请注明出处:http://blog.csdn.net/int64ago/article/details/7425727 看到这个标题无论你是处于怎样的心理进来看了,我觉得都是值得的.因为这个问题太 ...

  6. 算法与数据结构之二分查找

    一.两道LeetCode题 首先来两道算法题举例,来初步探讨二分查找 278.First Bad Version 先贴上代码 // Forward declaration of isBadVersio ...

  7. 查找算法:二分查找、顺序查找

    08年9月入学,12年7月毕业,结束了我在软件学院愉快丰富的大学生活.此系列是对四年专业课程学习的回顾,索引参见:http://blog.csdn.net/xiaowei_cqu/article/de ...

  8. 剑指Offer #06 旋转数组的最小数字(二分查找)| 图文详解

    题目来源:牛客网-剑指Offer专题 题目地址:旋转数组的最小数字 题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小 ...

  9. SAP ABAP二分查找(binary search)实际问题的深度分析

    在维护公司SAP的过程中,遇到一个问题,困扰了很久! 简单描述一下问题:(为了不牵扯公司业务,这是抽取问题)将主要的三个字段 存在一个内表TAB2,如图所示: 需要强调一下,真是的内表比TAB2要多很 ...

最新文章

  1. 孙正义60亿贱卖波士顿动力,狂降至3折,现代汽车接盘
  2. zoj2968 Difference Game
  3. Python爬虫之puppeteer之遇到的bug及解决方法
  4. mysql字段分隔符拆分_MySQL里实现类似SPLIT的分割字符串的函数
  5. xml2 交叉编译移植
  6. 数据库学生管理系统课程设计
  7. API函数的调用过程
  8. 剑指Offer——完美+今日头条笔试题+知识点总结
  9. w10怎么修改dns服务器,Win10怎么修改DNS 如何把DNS设置为百度公共DNS
  10. 第16周收录103起融资,芯片创企为热点丨潜在周波啊
  11. 深富策略短线获利了结为主
  12. linux 脚本里切换用户密码,shell,切换用户,执行指定,脚本
  13. openerp/odoo 权限解析
  14. 统计笔记3:statistical inference
  15. 十年匠心,让国漫精致到羽翼丰满!《老鹰抓小鸡》幕后制作分享
  16. 组建计算机网络通常采用3种模式,对等网的组建_计算机中的543原则_计算机网络工作模式(2)...
  17. HTTP权威指南与图解HTTP读书笔记
  18. NMS by Representative Region: Towards Crowded Pedestrian Detection by Proposal Pairing论文笔记
  19. 计算机联锁的64D半自动闭塞,64d半自动闭塞—本科毕业设计(论文).doc
  20. 36氪WISE2020新经济之王大会圆满落幕 超170位嘉宾纵论新经济

热门文章

  1. ValidationSummary控件
  2. 【自动驾驶】路径规划—— Dubins 曲线推导(基于向量的方法)
  3. (8)Microsoft office Word 2013版本操作入门_制作传单海报
  4. 计算机学院辩论赛,计算机科学学院辩论赛.doc
  5. JavaScript数组越界访问
  6. openwrt ipk 软件包安装教程
  7. 单纯形法python程序_Python-linprog最小化误差单纯形法
  8. 洛谷【CF817D】 Imbalanced Array
  9. 像间谍一样观察-杰克·谢弗
  10. 低代码时代:快速开发成为主流,全民开发不是梦