【LeetCode】35. 搜索插入位置

121 阅读1分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看: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

提示:

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

二分法

思路

写对「二分查找」的重点,从来不在于「二分查找」我们用的是哪一个模板(所有的模板背后的逻辑都一样),更不在于我们设置的区间是「左闭右闭」还是「左开右闭」。而在于认真看题、仔细分析题意,根据题目的条件和要求思考如何缩减区间,清楚地知道每一轮在什么样的情况下,搜索的范围是什么,进而设置左右边界。

我们需要分析清楚题目的意思,分析清楚要找的答案需要满足什么性质。应该清楚模板具体的用法,明白需要根据题意灵活处理、需要变通的地方,不可以认为每一行代码都是模板规定死的写法,不可以盲目套用、死记硬背。

二分查找只有一个思想,那就是:逐步缩小搜索区间。

本题解向大家介绍的,使用 left 和 right 向中间靠拢的方法,有一个非常强的语义,那就是:当 left 与 right 重合的时候,我们就找到了问题的答案,使用这种写法有一个巨大的好处,那就是返回值不需要考虑返回 left 还是 right,因为退出循环以后,它们是重合的。

在做题的过程中,会遇到两个难点:

  1. 取 mid 的时候,有些时候需要 +1,这是因为需要避免死循环;
  2. 只把区间分成两个部分,这是因为:只有这样,退出循环的时候才有 left 与 right 重合,我们才敢说找到了问题的答案(这两个难点,在练习的过程中,会逐渐清晰,不用着急一下子搞懂,事实上并不难理解)。

代码

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

结语

业精于勤,荒于嬉;行成于思,毁于随。