二分查找:思路很简单,细节是魔鬼
文章目录
- 1. 简介
- 2. 最简单的二分查找
- 3. 4种常见的二分查找变形问题
- 3.1 查找第一个值等于给定值的元素
- 3.2 查找最后一个值等于给定值的元素
- 3.3 查找第一个大于等于给定值的元素
- 3.4 查找最后一个小于等于给定值的元素
- Reference
1. 简介
二分查找(Binary Search)算法,也叫折半查找算法,二分查找针对的是一个有序的数据集合,查找思想有点类似分治思想,每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为0
二分查找的时间复杂度为: O ( l o g n ) O(logn) O(logn)
二分容易出错的细节:循环退出条件、区间上下界更新方法、返回值选择
二分查找的应用场景比较有限:底层必须依赖数组,并且要求数据有序;对于较小规模的数据查找,直接使用顺序遍历就可以了,二分查找的优势并不明显,二分查找更适合处理静态数据,也就是没有频繁的数据插入、删除操作
2. 最简单的二分查找
- 在不存在重复元素的有序数组中,查找等于给定值的元素
/*** 在不存在重复元素的有序数组中,查找等于给定值的元素** @param nums* @param target* @return*/
int binarySearch(int[] nums, int target) {int left = 0;int right = nums.length - 1; // 在[left, right]的范围里寻找targetwhile (left <= right) { // 当left==right时,区间[left,right]依然有效int mid = (left + right) >>> 1;if (nums[mid] == target) {return mid;} else if (nums[mid] < target) {left = mid + 1; // target在[mid+1,right]中} else if (nums[mid] > target) {right = mid - 1; // target在[left, mid-1]中}}return -1;
}
3. 4种常见的二分查找变形问题
3.1 查找第一个值等于给定值的元素
- 有序数据集合中存在重复的数据,希望找到第一个值等于给定值的数据
- 比如下面这样一个有序数组,其中, a[5], a[6], a[7]的值都等于8,是重复的数据。希望查找第一个等于8的数据,也就是下标是5的元素
/*** 查找第一个值等于给定值的元素* @param nums* @param target* @return*/int left_binarySearch(int[] nums, int target) {int left = 0;int right = nums.length - 1;while (left <= right) {int mid = (left + right) >>> 1;if (nums[mid] > target) {right = mid - 1;} else if (nums[mid] < target) {left = mid + 1;} else {if ((mid == 0) || (nums[mid-1] != target)) {return mid;} else {right = mid - 1;}}}return -1;}
- 当
nums[mid] > target
时,更新right=mid-1
- 当
nums[mid] < target
时,更新left=mid+1
- 当
nums[mid]==target
时,需要确认一下nums[mid]
是不是第一个等于给定值的元素- 若
mid = 0
,这个元素已经是数组的第一个元素,肯定是要找的 - 若
mid != 0
且 前一个元素nums[mid-1] != target
,也说明nums[mid]
就是要找的第一个值等于给定值的元素 - 若
mid != 0
且nums[mid-1]==target
,说明此时的nums[mid]
不是第一个值等于给定值的元素,就更新right=mid-1
,要找的元素肯定出现在 [ l e f t , m i d − 1 ] [left, mid-1] [left,mid−1]之间
- 若
3.2 查找最后一个值等于给定值的元素
- 有序数据集合中存在重复的数据,希望查找最后一个值等于给定值的元素
/*** 查找最后一个值等于给定值的元素* @param nums* @param target* @return*/
int right_binarySearch(int[] nums, int target) {int left = 0;int right = nums.length - 1;while (left <= right) {int mid = (left + right) >>> 1;if (nums[mid] > target) {right = mid - 1;} else if (nums[mid] < target) {left = mid + 1;} else {if ((mid == nums.length-1) || (nums[mid+1] != target)) {return mid;} else {left = mid+1;}}}return -1;
}
- 当
nums[mid] > target
时,更新right=mid-1
- 当
nums[mid] < target
时,更新left=mid+1
- 当
nums[mid]==target
时,需要确认一下nums[mid]
是不是最后一个等于给定值的元素- 如果
nums[mid]
这个元素已经是数组中的最后一个元素了,那它肯定是我们要找的 - 如果
nums[mid]
的后一个元素nums[mid+1]
不等于target
,那也说明nums[mid]
就是要找的最后一个值等于给定值的元素 - 如果经过检查之后,发现
nums[mid]
后面的一个元素nums[mid+1]
也等于target
,那说明当前的这个nums[mid]
并不是最后一个值等于给定值的元素。就更新left=mid+1
,因为要找的元素肯定出现在 [ m i d + 1 , h i g h ] [mid+1, high] [mid+1,high]之间
- 如果
3.3 查找第一个大于等于给定值的元素
- 在有序数组中,查找第一个大于等于给定值的元素。比如,数组中存储的这样一个序列: 3, 4, 6, 7, 10。如果查找第一个大于等于5的元素,那就是6
/*** 查找第一个大于等于给定值的元素* @param nums* @param target* @return*/
int greater_binarySearch(int[] nums, int target) {int left = 0;int right = nums.length - 1;while (left <= right) {int mid = (left + right) >>> 1;if (nums[mid] >= target) {if ((mid == 0) || (nums[mid-1] < target)) {return mid;} else {right = mid - 1;}} else {left = mid + 1;}}return -1;
}
- 如果
nums[mid] < target
,那要查找的值肯定在 [ m i d + 1 , r i g h t ] [mid+1, right] [mid+1,right]之间,所以,更新left=mid+1
。 - 如果
nums[mid] >= target
,要先看下这个nums[mid]
是不是要找的第一个值大于等于给定值的元素- 如果
nums[mid]
前面已经没有元素,或者前面一个元素小于要查找的值target
,那nums[mid]
就是要找的
元素 - 如果
nums[mid-1] >= target
,那说明要查找的元素在 [ l e f t , m i d − 1 ] [left, mid-1] [left,mid−1]之间,所以,更新right=mid-1
- 如果
3.4 查找最后一个小于等于给定值的元素
- 在有序数组中,查找最后一个小于等于给定值的元素。比如,数组中存储了这样一组数据: 3, 5, 6, 8, 9, 10。最后一个小于等于7的元素就是6
/*** 查找最后一个小于等于给定值的元素* @param nums* @param target* @return*/
int le_binarySearch(int[] nums, int target) {int left = 0;int right = nums.length - 1;while (left <= right) {int mid = (left + right) >>> 1;if (nums[mid] > target) {right = mid - 1;} else {if ((mid == nums.length-1) || (nums[mid+1] > target)) {return mid;} else {left = mid + 1;}}}return -1;
}
Reference
- 数据结构与算法之美
二分查找:思路很简单,细节是魔鬼相关推荐
- 【算法】详解二分查找算法(思路很简单,细节是魔鬼)
我周围的人几乎都认为二分查找很简单,但事实真的如此吗?二分查找真的很简单吗?并不简单.看看 Knuth 大佬(发明 KMP 算法的那位)怎么说的: Although the basic idea of ...
- 34. 在排序数组中查找元素的第一个和最后一个位置012(二分查找+思路+详解+两种方法)Come Baby!!!!!!!! !
一:题目 给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置. 如果数组中不存在目标值 target,返回 [-1, -1]. 进阶: 你 ...
- 81. 搜索旋转排序数组 II(014)二分查找+思路+详解+二种做法
一:题目 已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同. 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 ...
- 33. 搜索旋转排序数组(013)二分查找+思路详解+来干了这杯代码!!!!!!
一:题目 整数数组 nums 按升序排列,数组中的值 互不相同 . 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变 ...
- 二分查找和二分答案(洛谷)
细节好可怕~ 二分查找算法的细节剖析_JackComeOn的博客-CSDN博客原文:https://www.cnblogs.com/kyoner/p/11080078.html我周围的人几乎都认为二分 ...
- 查找算法——二分查找(原理+源码)
1,原理 二分查找又称折半查找,只适用于有序数组.二分查找原理很简单,针对有序数组的查找效率也很高.具体原理为,每次拿目标数值(以下用value表示)与数组中间位置的数据(以下用arry[mid]表示 ...
- 魔鬼一样的二分查找模板
Although the basic idea of binary search is comparatively straightforward, the details can be surpri ...
- LeetCode面试刷题技巧-二分查找算法代码思路解析
二分查找的思想 提及二分查找算法,我想大部分人都不陌生,就算不是学计算机的,基本上也都使用过二分查找的思想,不信的话,且听我慢慢为你道来. 不知道你有没有玩过这样一个游戏,猜数字.就是说一个人心里想了 ...
- 六十七、二分查找算法及其四个变形问题
@Author:Runsen 编程的本质来源于算法,而算法的本质来源于数学,编程只不过将数学题进行代码化. ---- Runsen 文章目录 二分法查找 二分查找的变形问题 查找第一个等于给定值的元素 ...
最新文章
- Windows 远程桌面连接数限制
- LAMP之二:LAMP的性能测试以及安装xcache,为php加速
- 仿联想商城laravel实战---7、lavarel中如何给用户发送邮件
- codeforces Round#429 (Div2)
- BA-Alerton系统简介
- pandas 数据索引与选取
- 关于页面布局的一些注意点
- bigpipe提升网站响应速度
- ROS笔记(20) Kinect仿真
- 4.1 深层神经网络
- maven 编译命令
- 矩阵运算_Sophus库的使用
- o(n)复杂度下实现数组去除重复项
- 【原】数据分析/数据挖掘/机器学习---- 必读书目
- InnoDB存储引擎架构
- 逻辑回归算法识别Minst手写集
- Windows 10不能安装mini版迅雷的解决方法
- java导出csv文件,使用Excel打开中文乱码
- 北京车管所 与 换领驾驶证过程
- WMS系统是什么——史上最全WMS介绍