此种二分查找的思路并不是为了直接去查找目标元素,而是不断地去排除不是目标的元素,退出循环时,数组中只有一个元素,那么这个元素很大可能性是目标元素,如果题目已经告诉你数组中有一个目标元素,那么这个铁定是目标元素
public class Solution {
public int search(int[] nums, int target) {
int len = nums.length;
int left = 0;
int right = len - 1;
// 目标元素可能存在在区间 [left, right]
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
// 下一轮搜索区间是 [mid + 1, right]
left = mid + 1;
} else {
// 下一轮搜索区间是 [left, mid]
right = mid;
}
}
if (nums[left] == target) {
return left;
}
return -1;
}
}
循环可以继续的条件是 while (left < right) ,这是一个很重要的标志。为什么是严格小于呢?我们上一节说过,当 left == right ,左边界和右边界重合的时候,区间里只有 1 个元素时候,二分查找的逻辑还需要继续下去;而现在大家看到的这个解法在 left == right 重合的时候就退出了循环,这一点表示区间里只剩下一个元素的时候,有可能这个元素就是我们要找的那个元素。这一点与二分查找算法的思路 2(在循环体中排除元素)是一致的:排除了所有错误的答案,如果题目告诉我们只有 1 个目标元素,那么剩下的这个元素就一定是目标元素。
第二点
关于向上取整和向下取整。
有关死循环的问题
如果是
int mid = left + (right - left)/2; 则是向下取整
这时如果你的判断时
while (left < right) {
int mid =left + (right - left) / 2;
if (nums[mid] > target) {
// mid 以及 mid 的右边一定不是目标元素最后一次出现的位置
// 下一轮搜索的区间是 [left, mid - 1]
right = mid - 1;
} else {
// 下一轮搜索的区间是 [mid, right]
left = mid;
}
}
return left;
如果最后只有两个元素,由于是向下取整,导致mid值是在left位置,但你最后判断的结果是left还是等于mid,那说明进入了死循环。
我们只需要记住一个结论:当看到分支是 left = mid; 和 right = mid - 1; 的时候,在计算中间数的索引的时候要向上取整。
left = mid+1 或者 right = mid-1
这里的加一和减一都要非常的考虑仔细,并不是随随便便就去 加一和减一的, 因为这里的 left 和 right 都是闭区间,这里的left 和 right 在你把区间一分为二的时候,这里的left 和 right 要能够取到值,记住,一定要能取到值,不然的话,就不是闭区间了,而是开区间了。