二分查找算法相关的题最重要的就是如何确定循环条件和搜索区间这两个细节:1. while循环的条件中
left<=right
和left<right
的确定;
2. 二分初始区间上界是nums.length-1
还是nums.length
。
选取三种典型的题型分别进行分析:
1. 寻找一个数(基本的二分查找)
public int binarySearch(int[] nums,int target) {
if(nums==null||nums.length==0){
return -1;
}
int mid,left=0,right=nums.length-1;
while(left<=right){
mid=left+(right-left)/2;
if(nums[mid]==target){
return mid;
}else if(nums[mid]>nums[right]){
left=mid+1;
}else{
right=mid-1;
}
}
return -1;
}
- 注意: 这里while循环条件为
left<=right
,二分初始区间上界right
是nums.length-1
。- 因为区间[0,nums.length-1]能覆盖到所有可能返回的结果
target
要么存在于这个区间,要么不存在返回-1。所以上界right
取nums.length-1
。 - while循环条件为
left<=right
, 因为查找范围是[left , right],循环终止的条件是left==right+1
,写成区间形式为[right+1 , right],这时区间为空就退出循环,说明target
不存在直接返回-1。 - while循环条件也可为
left<right
,循环终止的条件是left==right
,写成区间形式为[right , right],此时还有right
这个值未进入循环内判断就退出了循环,因此最后返回值部分需要写成return nums[right]==target?left:-1
。
- 因为区间[0,nums.length-1]能覆盖到所有可能返回的结果
2. 寻找第一个大于target的元素位置
public int upper_bound(int[] nums,int target) {
if(nums==null||nums.length==0){
return -1;
}
int mid,left=0,right=nums.length;
while(left<right){
mid=left+(right-left)/2;
if(nums[mid]>target){
right=mid;
}else{
left=mid+1;
}
}
return left;
}
- 注意: 这里while循环条件为
left<right
,二分初始区间上界right
是nums.length
。- 考虑到欲查询元素有可能比序列中的所有元素都要大,二分上界取
nums.length
,搜索区间为[0,nums.length]
,当返回值为nums.length
时,说明查询元素比序列中的所有元素都要大。 - while循环条件为
left<right
, 因为不需要判断查询元素本身是否存在,就算它不存在,返回的也是它应该在的位置,于是循环终止的条件是left==right
,写成区间形式为[right , right]
刚好能夹出唯一的位置。
- 考虑到欲查询元素有可能比序列中的所有元素都要大,二分上界取
3. 寻找旋转排序数组中的最小值(LeetCode 153)
public int findMin(int[] nums) {
if(nums==null||nums.length==0){
return -1;
}
int mid,left=0,right=nums.length-1;
while(left<right){
mid=left+(right-left)/2;
//左区间连续,最小值在右区间
if(nums[mid]>nums[right]){
left=mid+1;
}else{
right=mid;
}
}
return nums[left];
}
- 注意: 这里while循环条件为
left<right
,二分初始区间上界right
是nums.length-1
。- 因为区间[0,nums.length-1]能覆盖到所有可能返回的结果,这个区间一定能取到最小值。
- while循环条件为
left<right
, 循环终止的条件是left==right
,写成区间形式为[right , right]
刚好能夹出唯一的位置。
总结:
- while循环的条件中
left<=right
和left<right
的确定由问题本身决定,如果查询元素一定存在搜索范围内,选择left<right
可以夹出唯一的位置;而left<=right
能将搜索区间范围一步步缩小,一直到区间为空才会退出。 - 二分初始区间应该覆盖到所有可能返回的结果。
参考资料: 二分查找算法详解_labuladong.