一、需查找和目标值相等的数
比如数组 [2, 4, 5, 6, 9],target = 6,返回3,即target 的在数组中的位置。二分查找法的代码如下:
int find(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) left = mid + 1;
else right = mid;
}
return -1;
}
注意二分查找法的写法并不唯一,主要可以变动地方有四处:
第一处是 right 的初始化,可以写成 nums.length或者 nums.length- 1。
第二处是 left 和 right 的关系,可以写成 left < right 或者 left <= right。
第三处是更新 right 的赋值,可以写成 right = mid 或者 right = mid - 1。
第四处是最后返回值,可以返回 left,right,或 right - 1。
但是这些不同的写法并不能随机的组合,像博主的那种写法,若 right 初始化为了 nums.length,那么就必须用 left < right,而最后的 right 的赋值必须用 right = mid。但是如果我们 right 初始化为 nums.length - 1,那么就必须用 left <= right,并且right的赋值要写成 right = mid - 1,不然就会出错。
二、查找第一个不小于目标值的数
如果查找的目标值不一定会在数组中出现,也有可能是跟目标值相等的数在数组中并不唯一,而是有多个,那么这种情况下 nums[mid] == target 这条判断语句就没有必要存在。比如在数组 [2, 4, 5, 6, 9] 中查找数字3,就会返回数字4的位置;在数组 [0, 1, 1, 1, 1] 中查找数字1,就会返回第一个数字1的位置,即返回的位置就是 right 指针指向的地方。代码如下:
int find(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) left = mid + 1;
else right = mid;
}
return right;
}
如果变为查找最后一个小于目标值的数,因为已经找到了第一个不小于目标值的数,那么再往前退一位,返回 right - 1,就是最后一个小于目标值的数。
三、 查找第一个大于目标值的数
这里跟第二种写法上很相似,只需要添加一个等号,将之前的 nums[mid] < target 变成 nums[mid] <= target,这变化其实直接就改变了搜索的方向,使得在数组中有很多跟目标值相同的数字存在的情况下,返回最后一个相同的数字的下一个位置。
int find(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) left = mid + 1;
else right = mid;
}
return right;
}
与第二种一样,可以变形为查找最后一个不大于目标值的数。