本文正在参加「Java主题月 - Java 刷题打卡」,详情查看 活动链接
一、题目描述:
二、思路分析:
这道题要分情况讨论下,如果目标值存在,那就是一个普通的二分查找,不用多说。
那要是不存在呢?
已知: 升序——也就是找到第一个比target数值大的元素的索引,或者说最后一个比target数值小的元素的索引。
那么问题来了,如果没有升序呢(或者说在j>i的情况下,a[j] 可能等于 a[i])?
在这里假定插入位置为第一个比target数值大的元素的索引,那么如何找到这个位置呢?
以下对于范围的分析,务必注意开,闭区间的差别
if(arr[mid] == target){
// 这个时候可以知道所找的位置一定在(mid,right]之间
}else if(arr[mid] > target){
// 这个时候就可以知道所找的位置一定在 [left,mid]之间
}else if(arr[mid] < target){
// 这个时候就可以知道所找的位置一定在 (mid,right)之间
}// 其实这部分也就是else了,没必要else if 但我这么写你看的比较直观
在此代码基础上,我们不难发现arr[mid] == target和arr[mid] < target是有一定的重合的,尽管在mid的取值上有区别,但我们可以扩大一个的范围来合并if条件
if(arr[mid] > target){
// 这个时候就可以知道所找的位置一定在 [left,mid]之间
}else if(arr[mid] <= target){
// 这个时候就可以知道所找的位置一定在 (mid,right)之间
}// 其实这部分也就是else了,没必要else if 但我这么写你看的比较直观
我希望你可以进一步思考下为什么我们要合并(或者说什么时候代码是应该被合并的,什么时候简单使用模板就可以了。)
我相信你也为此而困扰
在上篇文章中我们将搜索分为了三个部分,这篇文章却只有两个部分,区别在于在这篇文章中,发现arr[mid] == target 并不能帮我们跳出循环,相反我们要靠while(left < right)来得到最终的结果
那就引出了第二个问题
while(left < right){}
//OR
while(left <= right){}
不妨将上篇文章和本文一起分析,
上篇文章在搜索时,如果arr[mid] == target可以跳出循环,因此只有在查询失败的时候,才会走完while循环,因此应该是
while(left <= right){}
反观这个问题,arr[mid] == target 没什么用,因此你是在走完循环时才能查询成功。在这种情况下,只有在遍历所有数据的时候才知道成功的位置,也就是在你搜索的空间只剩下一个数据时,即while(left < right)。
三、AC 代码:
public class Solution {
public int searchInsert(int[] nums, int target) {
int len = nums.length;
// 特殊判断
if (nums[len - 1] < target) {
return len;
}
// 程序走到这里一定有 target <= nums[len - 1]
int left = 0;
int right = len - 1;
// 在区间 nums[left..right] 里查找第 1 个大于等于 target 的元素的下标
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target){
// 下一轮搜索的区间是 [mid + 1..right]
left = mid + 1;
} else {
// 下一轮搜索的区间是 [left..mid]
right = mid;
}
}
return left;
}
}
四、总结:
这篇文章应该能回答我上篇文章中强调的几个问题,那么对于二分的几个应用场景,我会写在下篇文章中。