二分法应用场景
- 应用一:寻找一个数,例如:二分查找
- 应用二:寻找左侧边界,例如:在排序数组中查找元素的第一个和最后一个位置
-
二分法注意事项
1、left <= right 与 left < right的区别,以及它们什么时候使用
之所以例一要使用等号,这是因为初始化 right 的赋值是 nums.length - 1,即最后一个元素的索引,而不是 nums.length。例一和例二的区别是:前者相当于两端都闭区间 [left, right],后者相当于左闭右开区间 [left, right),因为索引大小为 nums.length 是越界的。left <= right表示 left = right + 1时结束,而left < right表示left =right 时结束,所以为了不越界就会与right的 边界值有关 是否使用等号,在于初始化rigth是等于nums.length还是等于nums.length - 1,当right = nums.length时就不需要等号,当right = nums.length - 1时就需要等号。注意当right =nums.length时,此时由于左边界不用去,所以在收缩边界的时候,right = mid即可,不用right = mid - 1,因为此时判断条件为left = right就结束,如果right = mid - 1的话,左边界的值就会取不到。
//例一public int search(int[] nums, int target) {int left = 0, rigth = nums.length - 1;while (left <= rigth) {int mid = (left + rigth) / 2;if (nums[mid] == target) return mid;if (nums[mid] > target) rigth = mid - 1;else left = mid + 1;}return -1;}//例二public int search(int[] nums, int target) {int left = 0, rigth = nums.length;while (left < rigth) {int mid = (left + rigth) / 2;if (nums[mid] == target) return mid;if (nums[mid] > target) rigth = mid;else left = mid + 1;}return -1;}
2、什么时候left = mid + 1,left = mid和right = mid - 1,right = mid
当初始范围为 [left, right]。那么当我们发现索引 mid 不是要找的 target 时,下一步应该去搜索哪里呢? 当然是去搜索 [left, mid-1] 或者 [mid+1, right] 对不对?因为 mid 已经搜索过,应该从搜索区间中去除。>
3、左边界的二分搜索 ```java int left_bound(int[] nums, int target) { if (nums.length == 0) return -1; int left = 0; int right = nums.length; // 注意
while (left < right) { // 注意
int mid = (left + right) / 2;if (nums[mid] == target) {right = mid;} else if (nums[mid] < target) {left = mid + 1;} else if (nums[mid] > target) {right = mid; // 注意}
} return left; }
//等价于上述代码 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; }
> 为什么上述代码可以搜索左边界,关键在于下面这段代码
> if (nums[mid] == target)
> right = mid;
> 可见,找到 target 时不要立即返回,而是缩小「搜索区间」的上界 right,在区间 [left, mid) 中继续搜索,即不断向左收缩,达到锁定左侧边界的目的。
- 右边界二分搜索,与左边界类似
```java
int right_bound(int[] nums, int target) {
if (nums.length == 0) return -1;
int left = 0, right = nums.length;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
left = mid + 1; // 注意
} else if (nums[mid] < target) {
left = mid + 1;
} else if (nums[mid] > target) {
right = mid;
}
}
return left - 1; // 注意
}
