leetcode 二分练习第二天

leetcode 35

参考 weiwei 哥的leetcode题解 总结一下 二分题目类型

关键思想 (排除法):把待搜索的目标值留在最后判断,在循环体内不断地把不符合题目要求的子区间排除掉,在退出循环以后,因为只剩下 1 个数没有看到,它要么是目标元素,要么不是目标元素,单独判断即可

在判断是选择 > 或者< 号时 一定确保 区间内 一定不存在 target 元素 才能 舍弃这部分区间

  1. 思路 : 将区间分为 两部分 一部分为一定不存在目标元素,另一部分可能存在目标元素,根据mid 分在 左右区间 分成两种情况。

  2. 一般步骤
    1 终止条件 严格写成left< right (退出时 left=right)

                while(left<right)
    

在这里插入图片描述
2 根据边界收缩行为 ,修改取中间的行为
int mid= left+(right-left)/2 (下取整 无法取到有边界)
3 退出时 判断是否需要 进一步检查 left 是否满足题目的要求

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        
        const int N=nums.size();
        if(N==0)  return 0;

        int l=0;
        int r=N;
        //if(target>nums[r]) return N;
        //因为有可能数组的最后一个元素的位置的下一个是我们要找的,故右边界是 len
        while(l<r)
        {
            int mid=(l+r)/2;

            if(target>nums[mid]){
                l=mid+1;
            }else{
                r=mid;
            }
        }

        return l;
    }
};

特别注意的是 right 边界的问题 的选择 取不到 right 元素 ,可以返回 right 下标

leetcoe 34

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]。

输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]

思考题目中关键点

首先确认左边界
[5,7,7,8,8,10] 当 mid 为 8 时 它之后的 8 一定不是 左边界(此处比较绕 需要思考一下),根据排除法则 确定不在此区间内的元素情况。
target 不在目标区间内
if(num[mid]<target) 下一次搜索区间为[mid+1,right]
left=mid+1
此时只需要考虑对立区间就行
else {
right=mid [left,mid]
}
并没有漏掉区间中的任何一个元素

确认有边界
[5,7,7,8,8,10] 当 mid 为 8 时 它之前的 8 一定不是 右边界
所以只需要向右收缩就好了 (==向左扩展)
区间没有 8 的时候
target 不在区间内
if(num[mid]>target) // 下次搜索区间 [left ,mid-1]
right=mid-1
else left=mid // 此时注意 超时问题

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res(2);
        int left=0;
        if(nums.empty()) 
        {
            res[0]=-1;
            res[1]=-1;
            return res;
        }
        int right=nums.size()-1;
         
        //  不是返回要插入数组的下标不用对 r进行考虑
        while(left<right){
            int mid=left+(right-left)/2;
            // 通过排除 没有元素的区间来确定元素  区间
            // 给定目标值 在数组开始的位置
            if(nums[mid]<target){
                left=mid+1;  //[mid+1, right]
            }else {
                right=mid;  //[left, mid]
            }
        }

        if(nums[left]==target)
            res[0]=left;
        else {
            res[0]=-1;
            res[1]=-1;
            return res;
        }
        left=0,right=nums.size()-1;
        while(left<right){
            int mid=left+(right-left+1)/2;
            //  若此区间  完全不含有 target 
            if(nums[mid]>target){
                 right=mid-1;    // 下一次搜索区间 [left mid-1]
            }else {
                left=mid;   // [mid, right]   特别注left=mid 发生死循环 
            }
            cout<<left;
        }
        res[1]=left;
        return res; 
    }
};

leetcode 275

class Solution {
public:

    // 在数组中找到一个数num[i ] 使得这个数  =num.len-i;
    //  看nums[mid] 和区间[mid,len-1]得长度  既 len-1-mid+1  
    //  返回nums 中得值   
    int hIndex(vector<int>& citations) {
        int len=citations.size();
        
        
        int left=0;
        int right=len-1;
        //   此种情况因为是存在得   
        if(len==0||citations[len-1]==0) return 0;

        while(left<right){
            int mid=(left+right)/2;
            //  比长度小,就得去掉这个值
            if(citations[mid]<len-mid) {
                left=mid+1;
            }else{
                // 比长度大是满足 区间得,我们应该继续rangmid 向左走 尝试看有没有跟小得 mid 
                // 类似求  含有重复元素得第一个值  区间需要向左收缩
                right=mid;
            }
        }
        
        // 返回区间长度
        return len-left;
    }
};

暴力算法有问题目前没想到那里有问题

class Solution {
public:
    int hIndex(vector<int>& citations) {

        // 采用暴力方法 

        // 有序得找第一个  num[i]  且len-i  = num[i];
        int res=0;
        int N=citations.size();
        // end()  指向最后一份元素得下一个元素
        if(N==0||citations.back()==0)  return 0;
        for(int i=0;i<=N-1;i++)
        {
            if(citations[i]==N-i)
            {
            
               return citations[i];
            }
        }
        return 1;
    }
};

leetcode69 x 得平凡根

class Solution {
public:
    int mySqrt(int x) {

        if(x<2) return x;
        long long i=0;
        long long j=x/2;

        while(i<=j){
            long long mid =(i+j)/2;
            long long res=mid*mid;
            if(res==x) return  mid;
            if(res<x) {
                i=mid+1;
            }else{
                j=mid-1;
            }
        }
        return j;  //返回 i 会报错 int8 8 通不过 
                   //return j时说明while里没有找到符合的mid 此时i大于j 要取小的
    }
};

来源

/继续使用万能模板
判断去掉中位数的分支
如果midmid<x,不可以去掉当前mid,举例来说,8的平方根是2,而22=4,就小于8,但不能去掉当前的mid值
2) 所以如果midmid>x,就可以去掉当前mid,以8举例,假如mid=3,33=9,肯定不是平方根,可以去掉当前的mid值
(3)
所以这样在不去掉中位数的分支就会是left=mid,这样当只剩下两个数字的时候,当mid为左边界时,且会进入不去掉中位数得分支时,这样left是不会变的,就会进入死循环,所以我们取得mid要用右中位数,让她只有两个数字的时候中位数取右边。
这样左边界就取到右边得数,此时left=right,就会跳出循环了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值