适用情况
- 数组为有序数组,同时数组中无重复元素(否则结果将不唯一)。
- 在有重复元素的数组中,二分查找的结果可以是符合条件的元素第一次出现的位置,重复使用二分查找,设定不同条件,可以确定一个区间。
注意事项
二分查找涉及的很多的边界条件。在二分查找的过程中,每一次对区间边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
二分查找中,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。两种区间定义将有不同的二分查找写法。
左闭右闭 [left, right]
- while (left <= right) 要使用 <= ,因为left == right是有意义的
- if (nums[middle] > target) 则下一次 right 要赋值为 middle - 1,因为当前这个nums[middle] > target 说明一定不是target,那么接下来要查找的就是左区间,并且结束下标位置就是 middle - 1
- 类似的,if (nums[middle] < target) 则下一次 left 要赋值为 middle + 1,因为当前这个nums[middle] < target 说明一定不是target,那么接下来要查找的就是右区间,并且结束下标位置是 middle + 1
时间复杂度:O(log n)
空间复杂度:O(1)
代码示例:
public int search(int[] nums, int target) {
int left=0;
int right=nums.length;
// 使用左闭右闭区间
right=right-1;
while(left<=right){
int middle=(left+right)/2;
if(nums[middle]>target){
right=middle-1;
}else if(nums[middle]<target){
left=middle+1;
}else if(nums[middle]==target){
return middle;
}
}
return -1;
}
左闭右开 [left, right)
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的。
- if (nums[middle] > target) 则 right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]。
- 同理,if (nums[middle] < target) 则 left 更新为 middle+1,因为middle位置已经比较过了,下一个查询区间从middle+1位置开始比较。
public int search(int[] nums, int target) {
int left=0;
int right=nums.length;
// 使用左闭右开区间
while(left<right){
int middle=(left+right)/2;
if(nums[middle]>target){
right=middle;
//middle位置已经比较过,下一次从middle-1开始比较,即右边开区间边界为middle
}else if(nums[middle]<target){
left=middle+1;
}else if(nums[middle]==target){
return middle;
}
}
return -1;
参考资料: