代码随想录 | Day01 | 数组:二分查找及其相关题目

代码随想录 | 刷题记录 | Day01 | C++

Day 01 主要学习内容

  1. 二分法基本使用
  2. 二分法找左右边界

二分查找

1 找到就行 适用于没有重复数
/*
关键点:
1.while(l<r) or while(l<=r)
2.r or l == mid+1 or mid-1 or mid
3.如果为了防止mid太大的话可以选择 mid=l+(r-l)/2;

主要取决于边界,分为两种
第一种是左闭右闭,第二种是左闭右开
*/
//第一种
//左闭右闭 区间 l<=r 因为r==l时 [l,r]是合法的,只有一个数字 eg:[1,1] 区间内有一个元素为1
//判断是等于mid+1或者-1要看到底是不是l和r在你接下来搜索的区间内
//这个如果有重复元素的话有随机性,可能找到左边界可能找到右边界可能找到左右边界中间的某个数
int search(vector<int>& nums, int target) {
        int l=0,r=nums.size()-1;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(nums[mid]>target) r=mid-1;
            else if(nums[mid]<target) l=mid+1;
            else return mid;
        }
        return -1;
    }
//第二种
//左闭右开 l<r 因为l=r是 [l,r)无意义 eg:[1,1) 中没有元素 和上面的第一种类似
int search(vector<int>& nums, int target) {
        int l=0,r=nums.size();
        while(l<r)
        {
            int mid=l+r>>1;
            if(nums[mid]>target) r=mid;//右开 所以r本来就不在区间内,那就可以等于mid而不用减1
            else if(nums[mid]<target) l=mid+1;
            else return mid;
        }
        return -1;
    }

1 如果有重复数字 用以下方法寻找左右边界
第一种
//如果没有重复元素两种方式等价,并且l和r都相等,如果有重复元素,则第一种为左边界,第二种为右边界都是可以取到的
//循环结束条件均为l==r
void bsearch_1(int l, int r)//寻找左边界 区间左闭右开
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (nums[mid]>=target) r = mid;//找左边界往左边缩
        else l = mid + 1;
    }
    l1 = l, r1 = r;
}

void bsearch_2(int l, int r)//寻找右边界 区间左开右闭 
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        // +1 原因在于:通过 (l + r) / 2 计算mid的值,结果是向下取整的。 在区间内只有两个元素的时,r的值可以用l + 1代替,因此mid = (l + r) / 2 = (l + l + 1) / 2 = l。 这个时候更新l = mid,l的值更新后依旧为l。
        if (nums[mid]<=target) l = mid;//找右边界往右边缩
        else r = mid - 1;
    }
    l2 = l, r2 = r;
}
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

vector<int> nums = { -1,0,3,5,5,5,5,5,9,12 };
int target = 5;

int l1, r1, l2, r2;

void bsearch_1(int l, int r)//寻找左边界 左闭右开
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (nums[mid]>=target) r = mid;
        else l = mid + 1;
    }
    l1 = l, r1 = r;
}

void bsearch_2(int l, int r)//寻找右边界 左开右闭
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (nums[mid]<=target) l = mid;
        else r = mid - 1;
    }
    l2 = l, r2 = r;
}

int main()
{
    bsearch_1(0,10);
    cout << l1<<" "<< r1 << endl;
    bsearch_2(-1,9);
    cout << l2<<" "<< r2 << endl;
    return 0;
}
//输出:
//3 3
//7 7

/*做 力扣34时发现
如果nums为单独一个数或者两个相同的数且小于target 如nums={3,3} target=5;
左边界会找不到,l,r在循环结束后会等于nums.size()
如果nums为单独一个数或者两个相同的数且大于target 如nums={6,6} target=5;
右边界会找不到,l,r在循环结束后会等于-1
即 此时的 l r均为非法状态
第二种(建议使用)
//左右边界均使用左闭右闭来寻找 上面提到可能会有非法状态故用first和last保存合法结果
//见下面
//34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int first=-1,last=-1;
        int l=0,r=nums.size()-1;
        while(l<=r)//左边界
        {
            int mid=(l+r)>>1;
            if(nums[mid]==target)
            {
                first=mid;//记录左边界
                r=mid-1;
            }
            else if(nums[mid]>target)
                r=mid-1;
            else
                l=mid+1;
        }
        l=0,r=nums.size()-1;
        while(l<=r)//右边界
        {
            int mid=(l+r)>>1;
            if(nums[mid]==target)
            {
                last=mid;//记录右边界
                l=mid+1;
            }
            else if(nums[mid]>target)
                r=mid-1;
            else
                l=mid+1;
        }
        return {first,last};
    }
};
704.二分查找

模板任选其一即可

35.搜索插入位置
//找左边界的模板
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int l=0,r=nums.size();
        while(l<r)
        {
            int mid=(l+r)>>1;
            if(nums[mid]>=target)
                r=mid;
            else
                l=mid+1;
        }
        return l;
    }
};
34. 在排序数组中查找元素的第一个和最后一个位置
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int first=-1,last=-1;
        int l=0,r=nums.size()-1;
        while(l<=r)//左边界
        {
            int mid=(l+r)>>1;
            if(nums[mid]==target)
            {
                first=mid;
                r=mid-1;
            }
            else if(nums[mid]>target)
                r=mid-1;
            else
                l=mid+1;
        }
        l=0,r=nums.size()-1;
        while(l<=r)//右边界
        {
            int mid=(l+r)>>1;
            if(nums[mid]==target)
            {
                last=mid;
                l=mid+1;
            }
            else if(nums[mid]>target)
                r=mid-1;
            else
                l=mid+1;
        }
        return {first,last};
    }
};

代码随想录的解法也很棒放在这里了

代码随想录 (programmercarl.com)

69.x的平方根
class Solution {
public:
    int mySqrt(int x) {
        int l=0,r=x;
        while(l<=r)
        {
            long long mid=(l+r)>>1;
            if(mid*mid==x)
                return mid;
            else if(mid*mid>x)
                r=mid-1;
            else   
                l=mid+1;
        }
        return r;//找的其实是左边界,下界 返回r(个人认为)
    }
};
367.有效的完全平方数
class Solution {
public:
    bool isPerfectSquare(int num) {
        int l=0,r=num;
        while(l<=r)
        {
            long long mid=(l+r)/2;
            if(mid*mid==num)
                return true;
            else if(mid*mid>num)
                r=mid-1;
            else    
                l=mid+1;
        }
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值