35. 搜索插入位置(C++)

题目

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

你可以假设数组中无重复元素。

示例 1:

输入: [1,3,5,6], 5
输出: 2

示例 2:

输入: [1,3,5,6], 2
输出: 1

示例 3:

输入: [1,3,5,6], 7
输出: 4

示例 4:

输入: [1,3,5,6], 0
输出: 0

分析与题解

暴力遍历

我们直接遍历给定排序数组nums中查询val值,大致会出现三种情况:

  • 数组中找到目标元素,因为数组中不存在重复元素,直接返回目标元素的对应下标即可
  • 数组中找不到目标元素,且存在元素大于目标值,首位大于目标值的下标就是插入的目标下标
  • 数组中找不到目标元素,且所有元素都小于目标值,直接将目标值插入到数组的末尾nums.size()

经过上述讨论,我们可以将第二、三情况判断条件归结到一起:对于排序数组,只要当nums[i] >= val,我们就可以返回当前下标。

完整代码如下:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int num = nums.size();
        for (int i = 0; i < num; i++) {
            if (nums[i] >= target)
                return i;
        }
        // 退出循环后则直接在数组末尾插入元素
        return num;
    }
};

二分查找(左闭右开)

注意右区间为开区间,所以初始化左右边界时,右边界可以赋值为数组的长度

实际上该下标是越界的,无法访问到

int left = 0, right = nums.size();

然后求取中值时取左中值,并且需要注意防止溢出:

int mid = left + (right - left) / 2;

对于中值下标我们与目标值进行比较:

  • nums[mid] == target,我们直接返回中值下标
  • nums[mid] < target哪怕target不在数组中,需要进行插入也不可能插入到当前位置,我们直接向后移位。那么target值可能的取值区间变为[mid + 1, right)
  • nums[mid] > target,此时不确定中值下标对应元素是否为首位大于目标值的元素,该下标有可能成为插入元素下标,需要进行保留。那么target值可能的取值区间变为[left, mid)

完整代码如下:

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

二分查找(左闭右闭)

注意讨论的区间为左闭右闭区间,所有右边界的初始下标必须是确实能够进行访问的:

int left = 0, right = num - 1;

关于中值的计算方式类似,但是因为是闭区间,无论nums[mid]target关系如何,都需要对中值mid进行加(减)一操作。

因为双闭区间的循环判断条件为while (left <= right),所以退出while循环后左右边界的关系为left = right + 1,总共可分为四种情况进行讨论:

  • 当数组中存在目标元素,即nums[mid] == target,那么循环中就可以返回目标下标
  • 当数组中不存在目标元素:
    • 当目标元素大于数组中所有元素,此时循环时右边界不会改变,左边界会一直向右移动,退出循环的区间为[nums.size() , nums.size() - 1]
    • 当目标元素小于数组中所有元素,此时循环时左边界不会改变,右边界会一直向左移动,退出循环的区间为[0, -1]
    • 当目标元素在取值在数组所有元素之间时,此时循环的左/右边界都会改变,退出循环的区间为[targetIndex, targetIndex - 1]

综上讨论,退出循环后关于插入元素下标的选择,要么选择left,要么选择right + 1

完整代码如下:

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int num = nums.size();
        int left = 0, right = num - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target)
                return mid;
            else if (nums[mid] < target)
                left = mid + 1;
            else if (nums[mid] > target)
                right = mid - 1;
        }
        return right + 1;
    }
};
posted @ 2020-12-30 11:28  脱线森林`  阅读(145)  评论(0编辑  收藏  举报