二分查找:35. 搜索插入位置 278. 第一个错误的版本

35. 搜索插入位置

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

请必须使用时间复杂度为 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

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 为 无重复元素 的 升序 排序

二分法:

class Solution {
    public int searchInsert(int[] nums, int target) {
        int n = nums.length;
        int left = 0,right = n - 1;
        while (left <= right) {
            int mid = ((right - left)>>1) + left;
            if (target <= nums[mid]) {
                right = mid - 1; 
            } else {
                left = mid + 1;
            }
        }
        return right+1;
    }
}

278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

示例 1:

输入:n = 5, bad = 4
输出:4
解释:
调用 isBadVersion(3) -> false 
调用 isBadVersion(5) -> true 
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。

示例 2:

输入:n = 1, bad = 1
输出:1

提示:

  • 1 <= bad <= n <= 231 - 1
/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */
// 找到第一次出现true位置索引,下标从1开始;
public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int left = 1,right = n;
        while (left < right) {
            int mid = left + ((right-left)>>1);
            if (isBadVersion(mid)) {
                right = mid;
            }else {
                left = mid + 1;
            }
        }
        return left;
    }
}

 

二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。例如到底是 while(left < right) 还是 while(left <= right),到底是right = middle呢,还是要right = middle - 1呢?

写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。

一、左闭右闭

       target在[left, right]区间,所以有如下两点:

    while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
    if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1

二、左闭右开

       target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式有如下两点:

    while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
    if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

视频详解:https://www.bilibili.com/video/BV1fA4y1o715/?spm_id_from=333.788&vd_source=5bd93cf27b18d0f110545d761892490f

posted on 2022-10-29 10:21  HHHuskie  阅读(20)  评论(0编辑  收藏  举报

导航