前提条件:
注意点:
int mid = (left + right) / 2; // left+right时可能int溢出
int mid = left + ((right - left) >> 1); //防止int溢出,同时使用位运算提高运算速度效率
第一种:区间左闭右闭类型[left,right]
while判断条件为while(left <= right) 使用<=,因为right是可以取到的
同时while内的转换使用的是 left = mid + 1 或 right = mid - 1;
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = left + ((right - left) >> 1);
if(nums[mid] < target) {
//因为nums[mid]不满足条件,并且区间左闭,left可以取到,所以应取left = mid+1,让区间无mid
left = mid + 1;
}else if(nums[mid] > target) {
//因为nums[mid]不满足条件,并且区间右闭,right可以取到,所以应取right = mid-1,让区间无mid
right = mid - 1;
}else{
return mid;
}
}
return -1;
}
第二种:区间左闭右开类型[left,right)
while判断条件为while(left < right) 使用<,因为right区间无法取到
同时while内的转换使用的是 left = mid + 1 或 right = mid
public int search(int[] nums, int target) {
int left = 0, right = nums.length;
while(left <= right) {
int mid = left + ((right - left) >> 1);
if(nums[mid] < target) {
//因为nums[mid]不满足条件,并且区间左闭,left可以取到,所以应取left = mid+1,让区间无mid
left = mid + 1;
}else if(nums[mid] > target) {
//因为nums[mid]不满足条件,并且区间右开,right不会取到,所以取right = mid就可以让区间无mid
right = mid;
}else{
return mid;
}
}
return -1;
}
举一个例子,在一个有序不重复数组中进行二分查找,当right=nums.length-1时,属于左闭右闭,选用第一种,当right=nums.length时,属于左闭右开,选用第二种。
根据具体的应用场景进行具体的修改,重在理解二分过程和边界。
第三种:数组有序,但包含重复元素,寻找重复元素的区间
有重复元素会导致返回的元素不唯一,例如力扣34在排序数组中查找元素的第一个和最后一个位置
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums == null || nums.length == 0)
return new int[]{-1,-1};
//1.寻找右边界,有与target相同的值的时候优先右偏
//最终的结果left返回的一定是第一个比target大的值的位置,所以它为右边界
int left = 0, right = nums.length - 1;
while(left <= right) {
int mid = left + ((right - left) >> 1);
if(nums[mid] <= target) {
left = mid + 1;
}else if(nums[mid] > target) {
right = mid - 1;
}
}
int right_bound = left;
//2.寻找左边界,有与target相同的值的时候优先左偏
//最终的结果right返回的一定是第一个比target小的值的位置,所以它为左边界
left = 0;
right = nums.length - 1;
while(left <= right) {
int mid = left + ((right - left) >> 1);
if(nums[mid] < target) {
left = mid + 1;
}else if(nums[mid] >= target) {
right = mid - 1;
}
}
int left_bound = right;
//3.判断是否存在target值区间
if(right_bound - left_bound - 1 < 1){
return new int[]{-1,-1};
}else{
return new int[]{left_bound+1,right_bound-1};
}
}
}
例题: