算法从二分开始1——在循环体内部排除元素(手中无模板,落笔即生花)|Java 刷题打卡

244 阅读3分钟

本文正在参加「Java主题月 - Java 刷题打卡」,详情查看 活动链接

一、题目描述:

4FLNXMJG3.png

二、思路分析:

这道题要分情况讨论下,如果目标值存在,那就是一个普通的二分查找,不用多说。

那要是不存在呢?

已知: 升序——也就是找到第一个比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] == targetarr[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;
    }
}

四、总结:

这篇文章应该能回答我上篇文章中强调的几个问题,那么对于二分的几个应用场景,我会写在下篇文章中。