参考文章
详解二分查找算法
ps. 这篇文章细节解释得非常好。
相关习题
寻找一个数:https://leetcode-cn.com/problems/binary-search/
寻找左右边界:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
核心思想
- 查找的区间为[left,right] 或者 [left,right) 。两种方式皆可,但注意代码前后的统一性。本篇笔记只使用[left,right]的区间表示方式。
- 循环的停止条件为搜索区间为空。
(1) 当查找区间为[left,right]时,循环为while(left<=right){...}
,循环结束时left == right+1
(2) 当查找区间为[left,right)时,循环为while(left<right){...}
,循环结束时有left == right。 - 使用mid切分数组。
(1) 使用[left,right]时,分割后的数组为[left,mid-1] 和 [mid+1,right],所以更新时left=mid+1,right=mid-1
。
(2) 使用[left,right)时,分割后的数组为[left,mid) 和 [mid+1,right),所以更新时left=mid+1,right=mid
。 - 注意事项:使用
int mid=left+(right-left)/2;
防止溢出(养成习惯!)
寻找一个数
class Solution {
public:
int search(vector<int>& nums, int target) {
int left=0, right=nums.size()-1;//区间为[left,right]
while(left<=right){ //循环前提条件:保证搜索区间不为空,==时仍有元素
int mid=left+(right-left)/2;//防止溢出
if(nums[mid]==target)
return mid;
else if(nums[mid]<target)
left=mid+1;//[mid+1,right]
else
right=mid-1;//[left,mid-1]
}
return -1;
}
};
寻找左边界
循环结束时,left表示数组中第一个nums[i]>=target
的索引,其中0<=left<=nums.size()。
因此,循环结束后需要
- 判断
left==nums.size()
- 根据
nums[left]==target
判断target是否存在。
class Solution {
public:
int searchRange(vector<int>& nums, int target) {
int n=nums.size();
//寻找左边界
int left=0, right=n-1;//[left,right]
while(left<=right){ //循环条件:搜索的区间不为空
int mid=(right-left)/2+left;//将数组拆分成[left,mid-1]和[mid+1,right]
if(nums[mid]>=target)
right=mid-1;
else
left=mid+1;
}
//循环结束时left==right+1。其中left为第一个满足nums[i]>=target的索引,0<=left<=n,需要判断nums[left]是否为所求
if(left==n || nums[left]!=target)
return -1;
return left;
}
};
寻找右边界
循环结束时,left表示数组中第一个nums[i]>target
的索引,其中0<=left<=nums.size(),需要判断left-1是否为要寻找的target。
因此,循环结束后需要
- 判断
left==0
- 根据
nums[left-1]==target
判断target是否存在。
class Solution {
public:
int searchRange(vector<int>& nums, int target) {
int n=nums.size();
//寻找右边界
int left=0, right=n-1;//[left,right]
while(left<=right){
int mid=(right-left)/2+left;//循环条件:搜索的区间不为空
if(nums[mid]<=target)
left=mid+1;
else
right=mid-1;
}
//循环结束时left==right+1。其中left为第一个满足nums[i]>target的索引,0<=left<=n,需要判断nums[left-1]是否为所求
if(left==0 || nums[left-1]!=target)
return -1;
return left-1;
}
};
拓展
二分查找的应用:除了寻找target左右边界的基本题型外,许多应用将二分查找和其他知识点包装在一起,这类问题的常见问法为寻找满足某条件的最小值/最大值。二分查找未必是这类题的最优解法,但不失为一种思想。
例1:https://leetcode-cn.com/problems/first-bad-version/
例2:https://leetcode-cn.com/problems/sqrtx/
例3:https://leetcode-cn.com/problems/split-array-largest-sum/