【每日算法】力扣35. 搜索插入位置

545 阅读3分钟

「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。

描述

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

示例 1:

输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:

输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:

输入: nums = [1,3,5,6], target = 7
输出: 4
示例 4:

输入: nums = [1,3,5,6], target = 0
输出: 0
示例 5:

输入: nums = [1], target = 0
输出: 0
 

做题

思路

因为这道题目要求了时间复杂度为O(log n),所以我们必须得了解二分法查找。

二分法查找就是给数组划定一个左右边界,再通过比较边界中间的数和目标数,然后决定边界该往哪里收缩,直到两个边界相交,就找到目标数在那个坐标上。

二分法查找大概如下。

private int binarySearch(int[] array,int target){
    int l = 0,r = array.length - 1;
    while (l<=r){
        int middle = (l+r)/2;
        if(target == array[middle]){
            return middle;
        }
        if (target > array[middle]){
            l = middle + 1;
        }else {
            r = middle - 1;
        }
    }
    return -1;
}

而这道题我们不需要找到这个数,又需要找到这个数。

因为我们不是直接查找这个数,所以不需要找到这个数后就返回 middle 了。

我们还需要知道目标数可以放在哪里。

如果目标数大于数组中的所有数,那就只能放在最后,下标为数组长度减一。

如果目标数小于数组中的所有数,那就只能放在第一位,下标为 0。

剩下的就是要放在数组中间的情况,我们使用二分法会找到一个跟目标数很接近的数的下标,目标数呢是要把数组中一个下标的数以及后面的数往后挤。

下面是我修改了上面的二分法查找的代码,把返回结果改成了 middle,以下是打印结果。

System.out.println(solution5.binarySearch(new int[]{1, 3, 5, 7}, 0)); //0
System.out.println(solution5.binarySearch(new int[]{1, 3, 5, 7}, 3)); //1
System.out.println(solution5.binarySearch(new int[]{1, 3, 5, 7}, 4)); //2
System.out.println(solution5.binarySearch(new int[]{1, 3, 5, 7}, 5)); //2
System.out.println(solution5.binarySearch(new int[]{1, 3, 5, 7}, 8)); //3

说明目标值处在数组中间时 middle 的一个情况:

  • 当数组中有这个数时,middle 就是这个数的下标,我们可以不可以放在这个下标上?可以。
  • 当数组中没有这个数时,下标为 middle 的数会大于目标值,我们可不可以放在这个下标上?可以

我们还可以看到当目标数小于数组中的所有数时,也是能够直接使用 middle 返回的,只有目标数大于数组中的所有数时,就不行, 8 把 7 往后挤,这个数组就不是递增的了。

我们可以发现,当目标数大于数组中的所有数时,在寻找 middle 的过程中是不会去移动右边界的,所以我们可以设置默认的返回的结果为数组的长度,然后只在移动右边界时才把 middle 赋值给返回结果。

运行的代码

 public int searchInsert(int[] nums, int target) {
    int l = 0,r = nums.length - 1;
    int res = nums.length;
    while(l<=r){
        int middle = (l+r)/2;
        if(nums[middle]<target){
            // 因为当前数小于 target,l 收缩
            l = middle + 1;
        }else{
            // 当前数大于 target,说明 target 可以放在这里
            res = middle;
            // 因为当前数大于 target,r 收缩
            r = middle - 1;
        }
    }
    return res;
}

image.png

最后

今天就到这里了。

这里是程序员徐小白,【每日算法】是我新开的一个专栏,在这里主要记录我学习算法的日常,也希望我能够坚持每日学习算法,不知道这样的文章风格您是否喜欢,不要吝啬您免费的赞,您的点赞、收藏以及评论都是我下班后坚持更文的动力。