二分查找(搜索插入位置)(3)

https://blog.csdn.net/2601_95366422/article/details/158662505

上节课链接

一.题目

35. 搜索插入位置 - 力扣(LeetCode)

二.思路讲解

题目给出的数组是升序排列的,这提示我们可以采用二分查找算法。需要考虑的是应该寻找左边界还是右边界。

实际上,无论是寻找左边界还是右边界都能解决这个问题。 当找不到与target完全匹配的元素时,该位置的值只有两种可能:

  1. 当前值小于target:

    • target应位于当前点右侧
    • 插入位置应在当前点之后
  2. 当前值大于target:

    • target应位于当前点左侧
    • 插入位置应在当前点之前

这里有一个关键点需要注意:插入新元素会影响后续元素的索引。具体来说:

  1. 找到正确的插入位置
  2. 将该位置及其之后的所有元素后移一位
  3. 将新元素放入空出的位置

这种操作会导致所有大于target的元素的索引都增加1。

例如:

  • 对于数组[1,3,5,6]和target=5,直接返回索引2
  • 对于数组[1,3,5,6]和target=2:
    • 二分查找会定位到值3(索引1)
    • 因为2<3,所以插入位置就是1
  • 对于数组[1,3,5,6]和target=7:
    • 二分查找会定位到最后一个元素6(索引3)
    • 因为7>6,所以插入位置是4(即数组末尾)

三.代码演示

cpp 复制代码
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) 
    {
        int n = nums.size();
        int left = 0;
        int right = n - 1;    
        while(left < right)
        {
            int mid = left + (right - left + 1) / 2;
            if(nums[mid] > target)
                right = mid - 1;
            else
            {
                left = mid;
            }
        }
        //第一种可能等于目标值
        if(nums[left] == target)
            return left;
        //第二种可能大于目标值
        else if(nums[left] > target)
            return left;
        //第三种可能小于目标值
        return left + 1;
    }
};

四.代码讲解

一、问题重述与算法选择

题目给定一个升序无重复 的整数数组和一个目标值,要求返回目标值的索引;若目标不存在,则返回它应该被插入的位置,即保持数组升序的下标。由于数组具备二分性 ,我们可以采用二分查找 在 O(log n) 时间内解决。本题既可以用左边界模板 ,也可以用右边界模板 ,这里讲解的是右边界二分查找的思路。

二、右边界模板的核心思想

右边界模板 的目标是找到最后一个小于等于目标值的位置。为什么这样能求出插入位置?

  • 如果目标存在,那么最后一个小于等于目标的位置就是目标本身(因为数组无重复元素)。

  • 如果目标不存在,那么最后一个小于等于目标的位置 就是最后一个比目标小的元素,插入位置就是该位置的下一个索引 。 这种思路与左边界(第一个大于等于目标)本质等价,但处理方式略有不同,需要结合循环结束后的分类讨论

三、循环条件与中点计算

采用 while (left < right) 作为循环条件,而不是 left <= right。这是因为当左右指针相遇时,区间内只剩一个元素,此时无需继续二分 ,可以直接在循环外进行判断。这种写法可以避免陷入死循环,并且符合边界收缩的逻辑。

中点计算采用向上取整 的公式,即 mid = left + (right - left + 1) / 2。当区间长度为偶数时,中点会偏向右边 ,这确保了当 leftright 相邻时,中点指向 right,从而保证区间能够正确收缩,不会出现 left 原地不动的情况。向上取整 是右边界查找的精髓

四、区间缩进规则

在循环中,根据中间值与目标值的比较结果,按照以下规则更新指针:

  • 当中间值大于目标时 :说明目标值一定在中间值的左侧 ,且中间值及其右侧所有元素都不可能是最后一个小于等于目标的位置。因此将右指针移动到中间值减一的位置,即舍弃右半区间

  • 当中间值小于等于目标时 :这里包含两种情况:中间值小于目标或等于目标。无论哪种,最后一个小于等于目标的位置可能在中间值本身,也可能在中间值右侧(因为右边可能还有等于或小于目标的数)。因此将左指针移动到中间值,保留中间值在搜索区间内 ,继续向右寻找。注意这里不能用 mid + 1,因为当中间值等于目标时,它可能就是右边界,必须保留。

五、循环结束后的处理

循环结束时,左右指针相等,此时指向的位置是数组中最后一个满足"小于等于目标"的候选位置。需要分三种情况讨论:

  1. 该位置的值等于目标:说明目标存在,直接返回该位置索引。

  2. 该位置的值大于目标 :这种情况意味着目标小于数组中的所有元素。因为循环结束时指向的是最后一个小于等于目标的位置,但实际上没有元素小于等于目标,指针最终会停在数组起始位置,而该位置的值大于目标。此时目标应该插入在数组最前面,即索引0,所以返回该位置即可。

  3. 该位置的值小于目标:这是最常见的情况,说明该位置是最后一个小于目标的值,那么目标应该插入到该位置之后,即返回该位置索引加一。

六、与左边界模板的对比

  • 左边界模板 直接寻找第一个大于等于目标 的位置,代码逻辑更简洁:采用向下取中 ,缩进规则为当中间值小于目标时左指针移到中间值加一,否则右指针移到中间值,循环结束后左指针即为答案,无需分类讨论

  • 右边界模板 需要向上取中,并在循环结束后根据三种情况分别处理,但逻辑对称,同样正确。两种模板的选择取决于个人习惯,理解其一即可类推另一。

相关推荐
咯哦哦哦哦2 小时前
windows下VSCode配置C++/CMake/Qt/MVSC 开发环境 【电脑已经安装vs2022】
c++·vscode·qt
XW01059992 小时前
5-11字典合并
数据结构·python·算法
wunianor2 小时前
[算法]2026年3月14日米哈游校招算法笔试题题解
算法
Elias不吃糖2 小时前
LeetCode-44 回溯解法
算法·leetcode·职场和发展
仟濹2 小时前
【算法打卡day25(2026-03-17 周二)今日算法:「回溯算法」】1-力扣17-电话号码的字母组合 2-力扣39-组合总和 3-力扣40-组合总和II
算法·leetcode·职场和发展
Storynone2 小时前
【Day26】LeetCode:452. 用最少数量的箭引爆气球,435. 无重叠区间,763. 划分字母区间
python·算法·leetcode
月明长歌2 小时前
【码道初阶-Hot100】LeetCode 3. 无重复字符的最长子串:从错误直觉到滑动窗口,彻底讲透为什么必须判断 `map.get(c) >= left`
java·算法·leetcode·哈希算法
艾莉丝努力练剑2 小时前
System V IPC内核实现精析
linux·运维·服务器·网络·c++·人工智能·学习
junnhwan2 小时前
LeetCode Hot 100——贪心算法
java·算法·leetcode