二分查找及例题

本文详细介绍了二分查找算法的基本概念,包括其在查找中点、搜索范围、插入位置、平方根计算、山脉数组峰顶索引、峰值查找、旋转排序数组中的最小值以及点名问题中的应用。通过实例讲解和优化策略,展示了二分查找的高效性及其在不同场景下的灵活运用。
摘要由CSDN通过智能技术生成

一、概念

时间复杂度O(logN)

知识点:

1

求中点

mid=left+(right-left)/2   对于偶数取到中点的左边

mid=left+(right-left+1)/2  对于偶数取到中点右边

对于奇数情况取到的中点都相同

2

不一定需要有序

二分实质是根据一个特定条件将区间分为两份

3

基础

while(left<=right)
{
    int mid=left+(right-left)/2;
    if(...)
        left=mid+1;
    else if(...)
        right=mid-1;
    else
        return ...;
}

查找左端点 :   即右区间包括ans

while(left<right)
{
    int mid=left+(right-left)/2;
    if(...)
        left=mid+1;
    else
        right=mid;
}

查找右端点: 即左区间包括ans

while(left<right)
{
    int mid=left+(right-left+1)/2;
    if(...)
        left=mid;
    else
        right=mid-1;
}

二、例题

1二分查找

1优化:防止溢出

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

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

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

以此题为例 

1,当ans被包括在右区间 即【0,index-1】【index,size-1】

index为ans对应的下标 

mid取到偶数中间的左边元素

取极限情况,当nums[mid]<target,当left为index-1时,right为index时,由于mid取到偶数中间的左边元素,则left+1就会跳出循环

 2,当ans被包括在左区间 即【0,index】【index+1,size-1】index为ans对应的下标 ,同理不多赘述

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.size()==0)
            return {-1,-1};//可能是空数组
        int left=0;
        int right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<target)
                left=mid+1;
            else
                right=mid;
        }
        int ans1=left;
        if(nums[left]!=target)
            return {-1,-1};
        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 {ans1,left};
    }
};

3搜索插入位置

二分:区间区分

第一个nums[mid]>=target

注意当target大于数组中所有值时

target的值应该为size()

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        //找第一个大于等于target的下标
        int left=0;
        int right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]<target)
                left=mid+1;
            else
                right=mid;
        }
        //当目标值全都大于数组元素
        if(target>nums[nums.size()-1])
            return nums.size();
        return left;
    }
};

4 x 的平方根 

二分:区间区分

最后一个mid*mid<=x

 一开始还以为要自己创个1到n的数组,但仔细想想这样时间空间复杂度不都是On了

后来在想想直接用left=0,left一直加,right=n,right一直减不就行了

这样时间复杂度0logn,

class Solution {
public:
    int mySqrt(int x) {
        if(x==0)
        return 0;
        int left=1;//注意是从1开始
        int right=x;
        while(left<right)//找最后一个下表对应的值的平方小于等于x的值
        {
            long long mid=left+(right-left+1)/2;
            if(mid*mid<=x)//这里有溢出的风险 用long
                left=mid;
            else
                right=mid-1;
        }
        return left;
    }
};

5山脉数组的峰顶索引**

二分:区间区分

第一个arr[mid]<arr[mid-1]

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

6寻找峰值

和题五本质一样 因为只需要找到一个值就行

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

7找旋转排序数组中的最小值

二分:区间区分

第一个nums[mid]<nums[0]

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums[0]<nums[nums.size()-1])//特判
            return nums[0];
        int left=0;
        int right=nums.size()-1;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(nums[mid]>=nums[0])//注意是大于等于 不是大于
                left=mid+1;
            else
                right=mid;
        }
        return nums[left];
    }
};

8点名

注意特判是最后一个取到n

二分:区间区分

第一个mid<nums[mid]

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

  • 14
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值