二分查找主要是对于一个有序数组的查找,所以在处理之间,需要保证数组的有序,同时在处理的时候,重要的是对于边界的处理,所以在处理上,使用else if的处理会更好一些。
使用二分查找的时候,主要是分为两种,一种是直接得出数据在数组中的位置,还有一种是得到数据的左右边界。
种类一
查找一个数,存在就返回索引位置,不存在的返回-1,这是最熟悉的,也是最基本的。
直接给出解法:
int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int mid = (right + left) / 2;
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else if (nums[mid] > target)
right = mid - 1;
}
return -1;
}
注意点:
- 这里的右边界是
nums.length - 1
,是不会越界的,所以在while循环中是可以取到右边界的。 - 结束循环的条件是 right ==left
- 如果右边界变成了
num.length
,那就没法取到,那么在循环中就变了left<right
种类二
左边界处理
种类一中缺少了对于边界的处理,比如在 数组为[1,1,1,3,4]中 返回数据1在数组中的左边界。
和种类一进行对比,
- 种类二在相等的情况下,从相等变成了想左缩小范围,缩小右边界,可以假设在mid的左侧,可能还存在与mid相同的值。
- 由于在相等的情况下,会继续执行,那么退出循环的循环条件变成了
left> right
,那么left 就有可能会出现越界的情况,需要进行判断。 - 在2中往下思考,会出现退出循环的条件的话,肯定要经历
left==tight
,之后经历一次循环之后,就退出循环,那么在最后一次循环后,num[left]可能不等于target,所以需要校验。
解:
int left_bound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
// 搜索区间为 [left, right]
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
// 搜索区间变为 [mid+1, right]
left = mid + 1;
} else if (nums[mid] > target) {
// 搜索区间变为 [left, mid-1]
right = mid - 1;
} else if (nums[mid] == target) {
// 收缩右侧边界
right = mid - 1;
}
}
// 检查出界情况
if (left >= nums.length || nums[left] != target)
return -1;
return left;
}
右边界处理
右边界的处理 和左边界的处理都是对称的,直接给出解
int right_bound(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid - 1;
} else if (nums[mid] == target) {
// 这里改成收缩左侧边界即可
left = mid + 1;
}
}
// 这里改为检查 right 越界的情况,见下图
if (right < 0 || nums[right] != target)
return -1;
return right;
}