【Leetcode二分查找】『在排序数组中查找元素的第一个和最后一个位置&搜索插入位置』

🎬 个人主页MSTcheng · CSDN
🌱 代码仓库MSTcheng · Gitee
🔥 精选专栏 : 《C语言
数据结构
《算法学习》
C++由浅入深

💬座右铭: 路虽远行则将至,事虽难做则必成!


上期我们给大家介绍了朴素的二分模板,本期我们就进入二分的进阶,从原来的查找一个数,进阶到查找两个数,且这两个数是一个区间的左右边界。

我们先来看题:

一、34. 在排序数组中查找元素的第一个和最后一个位置

1.1题目解析

1.2算法原理

1、暴力解法: 就是去遍历一遍原数组,而且还要人为去判断边界情况,返回第一个出现target值位置的下标,和最后一个出现target值位置的下标,时间肯定是O(N)。会超时这里就不展示了。

2、二分查找:
1、查找区间的左端点:


以上是本题查找区间左端点的算法,以题目的示例1为例,示例1让我们找到数组中等于target值的初始位置和结束位置:也就是下标3和下标4。上面的算法就是在查找下标3! 下面我们再来看看查找区间右端点,也就是查找4!

2、查找区间右端点:

查找右端点判断逻辑说明:

1、当x<=t此时目标值在左区间 所以不断二分缩小左区间直到找到最终结果 left=mid 注意left不能超过mid因为某一次的mid所对应的值可能就是右端点。
2、当x>t说明mid右区间一定不含目标值 ,所以直接大胆将right=mid-1,将right直接更新到mid-1,因为mid处的值已经大于目标值了,而mid-1的值不确定,所以将right更新到mid-1处 再继续二分。 一定能分到x<=t的那一个区间,等mid处于那一个区间了以后就按照第一步,不断缩小左区间即可!

1.3代码编写

cpp 复制代码
class Solution 
{
public:
    vector<int> searchRange(vector<int>& nums, int target) 
    {
        //定义两个指针
        int left=0,right=nums.size()-1;
        vector<int> ret;
        int begin=0;
        //处理特殊情况
        if(nums.size()==0)
        {
            return ret={-1,-1};
        }

        //二分查找
        while(left<right)
        {
            //细节二 一定是(right-left)/2 这样mid算的才是偏左的点
            int mid=left+(right-left)/2;
            if(nums[mid]<target)
            {
                //使用mid 分成左右两个区间 左区间的值都小于target 右区间的值都大于或等于target
                //更新左区间
                left=mid+  1;
            }
            else //(nums[mid]>=target)
            {
                //此时区间里面包含目标值 缩小右指针
                right=mid;
            }

        }
        //跳出循环 left==right 此时已经得到结果了

        if(nums[left]!=target)
        {
            return ret={-1,-1};
        }
        begin=left;//标记一下左端点

        //⼆分右端点
        //这里一定要重新计算 因为上面的right已经发生变化了
        left = 0, right = nums.size() - 1; 
        while(left < right)
        {	
        	//注意 查找右端点求中点要加一
            int mid = left + (right - left + 1) / 2;
            if(nums[mid] <= target) 
            {
                left = mid;
            }
            else 
            {
                right = mid - 1;
            }
        }
        return {begin, right};


        //查找右边界
        //法二:也可以根据左边界 增加一个计数器去计算该区间target值的个数
        //最后使用左端点加上个数就等于右端点的下标了
        // int i=0;
        // auto it=nums.begin()+left;
        // while(it!=nums.end())
        // {
        //     if(*it==nums[left])
        //     {
        //         i++;
        //     }
        //     it++;
        // }

        // return ret={left,right+i-1};
    }
};

二、35. 搜索插入位置

2.1题目解析

2.2算法原理

2.3代码编写

cpp 复制代码
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {

        int left=0,right=nums.size()-1;
        int mid=0;
        while(left<= right)
        {
            // 计算中间索引,避免 left + right 溢出(C++ 中整数溢出会导致未定义行为,此写法为最佳实践)!!!
            mid=left+(right-left)/2;
            if(nums[mid]<target)
            {
                //往右边去查找
                left=mid+1;
            }
            else if(nums[mid]>target)
            {
                //往左边去查找
                right=mid-1;
            }
            else
            {
                //找到了就直接返回该下标
                return mid;
            }
        }
        //没找到按顺序返回插入的位置 此时left被更新成了left+1
        return left;
    }
};

三、69. x 的平方根

3.1题目解析

3.2算法原理

3.3编写代码

cpp 复制代码
class Solution {
public:
    int mySqrt(int x) {
    	//处理一下特殊情况 
        if(x<=1)
        {
            return x;
        }
        //使用二分  right最多到x,因为给出的x一定在0------x^2中
        long left=1,right=x;
        long mid=0;
        while(left<right)
        {
            //使用偏右的中间值求法
            mid=left+(right+1-left)/2;
            if(mid*mid<=x)
            {
                //更新左区间
                left=mid;
            }
            else
            {
                //更新右区间
                right=mid-1;
            }
        }
        return (int)left;
    }

};

四、852. 山脉数组的峰顶索引

4.1题目解析

4.2算法原理

注意:
arr[mid]>arr[mid-1] ->left=mid 是将目标值放在左区间,然后不断缩小左区间直至left==right,就找到了最终的目标值。
arr[mid]<arr[mid-1] ->right=mid-1 当所计算的mid值在右边递减的区间时,因为我们要把目标值放在左区间,所以递减区间一定不含有目标值,直接大胆的将right更新到mid-1的位置 ,直到缩小到左区间,然后就按照上面的逻辑继续执行直到找到结果为止!!!

4.3编写代码

cpp 复制代码
class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) {
        int left=0,right=arr.size();

        while(left<right)
        {
            //使用偏右的二分
            int mid=left+(right-left+1)/2;
            if(arr[mid]>arr[mid-1])
            {
                //更新左区间
                left=mid;
            }
            if(arr[mid]<arr[mid-1])
            {
                //此时要往左区间里面去查找
                //更新右区间
                right=mid-1;
            }
        }
        return left;
    }
};

五、总结

1、二分算法的核心:

就是去找一个二段性能够找到一个mid对于的值,mid左边的区间都小于它,mid右边的区间都大于它。或者mid左边的区间都是单调递增,mid右边的区间都是单调递减。以及其他的二段性...

2、求中点的计算方法:

如果是把目标值放在左边区间,那么就使用求区间右端点的查找逻辑,如果是把目标值放在右边区间,那么就使用求区间左端点的查找逻辑。以第一题为模板,后面几题全都是第一题的变形,都是使用计算区间左右端点的查找逻辑。

相关推荐
初夏睡觉1 小时前
笔记(动态规划(引入)1)
笔记·算法·动态规划
热爱生活的猴子1 小时前
二分查找类算法题核心笔记
数据结构·笔记·算法
大模型实验室Lab4AI2 小时前
GDPO:多目标强化学习高效优化新路径
人工智能·深度学习·算法·机器学习
小O的算法实验室2 小时前
2026年CIE SCI2区TOP,用于地质灾害监测的配备自主对接站的无人机多航次路径规划,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
仟濹2 小时前
【算法打卡day9(2026-02-14 周六)算法:并查集】 4-卡码网108-冗余连接
算法
hoiii1872 小时前
拉丁超立方抽样(LHS)的MATLAB实现:基本采样与相关采样
开发语言·算法
不想看见4042 小时前
旋转数组查找数字--力扣101算法题解笔记
数据结构·算法
好学且牛逼的马2 小时前
【Hot100|24-LeetCode 141. 环形链表 - 完整解法详解】
算法·leetcode·链表
yxc_inspire2 小时前
2026年寒假牛客训练赛补题(六)
算法