[LC优选算法#6] 二分 | ⼆分查找 | 元素首尾位置 | 搜索插入位置 | x的平方根

1. 二分查找算法

之前的文章中介绍了基础的二分查找算法的模板,可移步至:

算法:二分查找【模板】

本篇文章中介绍的是基础的二分查找,即在有序数组 中使用二分算法。但二分查找基本适用于所有具有二段性的数组,不仅仅是有序数组。

了解二分:

  • 时间复杂度O(logN)
  • 循环结束的条件:两指针从数组首尾开始以每次砍掉一半长度的速率靠近,直至两指针相遇(left < right)
  • 正确性:根据二段性的特点,在暴力枚举的基础上,省去了不必要的枚举

2. 例题分析:

2.1 二分查找

由逐个枚举的暴力解法优化而来,标准的二分模板,难度不大:

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

        return -1;
    }
};

2.2 在排序数组中查找元素首尾位置

在排序数组中查找元素首尾位置

解题思路:

由于数组是按照非递减顺序排列的,因此可以使用二分算法来优化暴力枚举,时间复杂度为O(logN)。接下来我们对左右边界分别分析:

  • 左边界 :当nums[mid] == target时,right指针要继续右移 ,这样最后指针相遇的位置才是左边界,因此可以合并为当nums[mid] >= target时,right = mid
  • 右边界 :当nums[mid] == target时,left指针要继续左移 ,这样最后指针相遇的位置才是右边界,因此可以合并为当nums[mid] <= target时,left = mid

细节处理:

Q1while循环内部的判断条件是left < right还是left <= right

A1 :当出现left == right的情况时无需继续判断(例如当数组元素全大于 / 小于target时),因此判断条件为left < right

Q2 :求mid的时候需要+1吗?

A2 :当元素个数为奇数 时,+1没有影响;当元素个数为偶数 时,如果判断条件为第二种情况时,不+1会陷入死循环:

从上图我们可以总结出当出现right - 1时(第二种情况的指针移动方式),需要在mid中+1处理。

cpp 复制代码
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target)
    {
        int n = nums.size();
        if(n == 0) return {-1, -1}; //处理特例
        
        int left = 0, right = n-1;
        int mid;
        while(left < right)
        {
            mid = left + (right - left) / 2;
            if(nums[mid] >= target) right = mid;
            //当遇到target时,right还会向左收缩,直至找到最左边的target
            else left = mid + 1;
        }
        int retleft = right;
        if(nums[left] != target) return {-1, -1};

        left = retleft, right = n-1;
        while(left < right)
        {
            mid = left + (right - left + 1) / 2;
            if(nums[mid] <= target) left = mid;
            //当遇到target时,left还会向右收缩,直至找到最右边的target
            else right = mid - 1;
        }
        return {retleft, left};
    }
};

2.3 搜索插入位置

搜索插入位置

数组为升序,且需要查找插入位置的对应下标,符合二分条件,因此使用二分算法。

解题思路:

当元素存在 时,可以直接通过二分返回对用下标;不存在 时,返回的位置是该元素应该插入位置的下标,而这个位置的元素是比target大的,因此在处理right的时候不能出现right = mid - 1,否则会跳过该元素,由此可以倒退出判断条件。

特判 :当所有元素都小于 target时,二分的结果会落在最后一个元素的位置,但实际上我们想让它落在最后一个元素的下一个位置,因此需要对这种情况进行特判。

cpp 复制代码
class Solution {
public:
    int searchInsert(vector<int>& nums, int target)
    {
        int left = 0;
        int right = nums.size() - 1;

        if(target > nums[right]) return right + 1; //特判

        while(left < right)
        {
            int mid = (left + right) / 2;
            if(nums[mid] >= target)
            {
                right = mid;
            }
            else
            {
                left = mid + 1;
            }
        }

        return left;
    }
};

2.4 x 的平方根

x 的平方根

解题思路:

由于题目中要求不允许使用内置函数和相关运算符,因此我们可以从2开始枚举,直至找到乘方为x的对应根。在这个过程中我们发现枚举过程是递增 的,因此可以看作是一个0~x的递增数组,使用二分算法解决。

分类讨论:

  1. 当两数相乘刚好为x 时,直接返回这个数
  2. 当两数相乘大于x 时,返回这个数-1

注意点 :两数相乘的范围可能会超过int的范围,对此应该有意识地用long long存储。

cpp 复制代码
class Solution
{
public:
    int mySqrt(int x)
    {
        long long i = 0;
        
        for (i = 0; i <= x; i++)
        {
            if (i * i == x)
            {
                return i;
            }

            if (i * i > x)
            {
                return i - 1;
            }
        }
        
        // 控制所有路径都有返回值
        return -1;
    }
};

// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~

相关推荐
码流子2 分钟前
2026 图像数据标注工具横评:LabelImg / CVAT / Label Studio / X-AnyLabeling / 国产Web平台,到底怎么选?
大数据·人工智能·算法
hansang_IR2 分钟前
【题解】[JSOI2016] 最佳团体(分数规划)
c++·算法
代码中介商5 分钟前
C++ 预约系统实战(二):TCP + JSON 自定义协议设计
c++·网络协议
渡我白衣14 分钟前
Util工具类功能设计与类设计
linux·服务器·网络·c++·人工智能·目标检测·机器学习
羊羊小栈18 分钟前
基于「YOLO目标检测 + 多模态AI分析」的公共场所暴力安全智能检测分析预警系统
人工智能·算法·面试·毕业设计·大作业
小王的创意工坊18 分钟前
C++ 在线判题平台(OJ)测试报告
c++·可用性测试
土星云SaturnCloud27 分钟前
ResNet-50 图像分类算法在边缘微服务器上的部署与性能评测
服务器·人工智能·算法·边缘计算·resnet-50
Niuguangshuo30 分钟前
文本-音频时间戳对齐:5 个开源工具实测
算法·开源·音视频
Niuguangshuo34 分钟前
论文解读:RNN-Transducer,端到端流式 ASR 的骨架
算法·语音识别
sdm07042742 分钟前
仿muduo库实现高并发服务器-下
开发语言·网络·c++·多路转接