二分查找:力扣34.在排序数组中寻找第一个和最后一个位置(c++)

时间复杂度

假设运气最差时需要查找k次,每次寻找的区间会被缩小到 1 / 2 ; 1 / 4 ; 1 / 8 ; 1 / 16 ; 1 / 2 k 1/2;1/4;1/8;1/16;1/2^k 1/2;1/4;1/8;1/16;1/2k。最终找到时是范围缩小到1了,所以是 n / ( 2 k ) = 1 n/(2^k)=1 n/(2k)=1,所以最终 k = l o g 2 n 简写为 l o g n k=log_2n简写为logn k=log2n简写为logn

所以有 l o g n logn logn这个复杂度要求的可以去考虑二分法

34.在排序数组中寻找第一个和最后一个位置
具体实现

1.首先想好函数和对应的功能,并且考虑一些边界情况

应当将二分查找的逻辑放到另外的函数中去,左边界用二分查找,右边界如**果用线性查找的话会不会更快呢?**不一定,当target值的重复次数不多的时候会比重新二分查找快,但是如果target的数量为n,那么时间复杂度会上升到 o ( n ) o(n) o(n),所以做两次二分查找才能达到题目中的 o ( l o g n ) o(logn) o(logn)的要求。 2 l o g n 的复杂度也是 l o g n 2logn的复杂度也是logn 2logn的复杂度也是logn

边界情况:nums=[],即长度为0直接返回[-1,-1]

原理就是不断的移动区间,让left=right从而锁定一个数

vector<int> searchRange(vector<int>& nums, int target) {
if (nums.size()==0) return vector<int> {-1,-1};  //不要写[-1,-1]因为要求要是一个vector;
int begin_position = binarySearch_begin(nums,target);
if (begin_position==-1) return vector<int> {-1,-1};
int end_position = binarySearch_end(nums,target);
return vector<int> {begin_position,end_position};
}

2.编写开始位置的二分查找

要注意:

  • 退出条件 可以是left=right 写作while(left<right),或者left>right 写作while(left<=right)
  • 二分法的移动规则,移动后区间的开闭要思考清楚
  • 算法是否保证能找到target,如果不能请加入判断
int binarySearch_begin(vector<int>& nums,int target){  // 为什么是&?
    //开始位置的二分查找
    // 1.初始化左右边界
    int left=0,right=nums.size()-1;
    // 2.确定退出二分查找条件:左右指针与mid相等
    while(left<right){ // 确保在退出循环的时候left一定等于right
        int mid=(left+right)/2;   // 或者用位移
        // 注意是nums[mid],而不是mid说明区间需要右移,left移动[mid+1,right]
        if(nums[mid]<target) left=mid+1; 
        // 说明 第一个 target肯定不在mid右边,即target位置<=mid,所以区间变成[left,mid]
        else if(nums[mid]==target)  right=mid;
        // nums[mid]>target,说明target肯定不在mid的右边,变为[left,mid-1]
        else right=mid-1;
    }
    // 条件只能保证left==right,不能保证一定找到了target
    if(nums[left]==target) return left;
    else return -1;
}

3.结束位置的二分查找

要注意:

  • 如果转移的条件是: left=mid 那么mid的条件就是mid=(left+right+1)/2

​ 这个是在实际跑代码的时候才注意到的,例如left=4,mid=5会进入死循环,nums={5,7,7,8,8,10},target=8的情况下。因为left此时等于mid,(right+left)/2也等于mid,所以就一直循环,要加一的话才可以。

所以在刷题时确定开闭区间,要思考如果最后区间只剩下一个数或者两个数,自己的写法是否会陷入死循环,如果某种写法无法跳出死循环,则考虑尝试另一种写法。

4.完整代码

class Solution {
public:
    int binarySearch_begin(vector<int>& nums,int target){  // 为什么是&?
        //开始位置的二分查找
        // 1.初始化左右边界
        int left=0,right=nums.size()-1;
        // 2.确定退出二分查找条件:左右指针与mid相等
        while(left<right){ // 确保在退出循环的时候left一定等于right
            int mid=(left+right)/2;   // 或者用位移
            // 注意是nums[mid],而不是mid说明区间需要右移,left移动[mid+1,right]
            if(nums[mid]<target) left=mid+1; 
            // 说明 第一个 target肯定不在mid右边,即target位置<=mid,所以区间变成[left,mid]
            else if(nums[mid]==target)  right=mid;
            // nums[mid]>target,说明target肯定不在mid的右边,变为[left,mid-1]
            else right=mid-1;
        }
        // 条件只能保证left==right,不能保证一定找到了target
        if(nums[left]==target) return left;
        else return -1;
    }

    int binarySearch_end(vector<int>& nums,int target){  // 为什么是&?
        //结束位置的二分查找
        // 1.初始化左右边界
        int left=0,right=nums.size()-1;
        // 2.确定退出二分查找条件:左右指针与mid相等
        while(left<right){ // 确保在退出循环的时候left一定等于right
            int mid=(left+right+1)/2;   // !!如果left=mid则mid=(l+r+1)
            // 注意是nums[mid],而不是mid说明区间需要右移,left移动[mid+1,right]
            if(nums[mid]<target) left=mid+1; 
            // 说明 最后 target肯定不在mid右边,即target位置>=mid,所以区间变成[mid,right]
            else if(nums[mid]==target)  left=mid;
            // nums[mid]>target,说明target肯定不在mid的右边,变为[left,mid-1]
            else right=mid-1;
        }
        // 条件只能保证left==right,不能保证一定找到了target
        return right;
        // 不需要,因为找开始位置的时候验证过了,else return -1;
    }


    vector<int> searchRange(vector<int>& nums, int target) {
        if (nums.size()==0) return vector<int> {-1,-1};  //不要写[-1,-1]因为要求要是一个vector;
        int begin_position = binarySearch_begin(nums,target);
        if (begin_position==-1) return vector<int> {-1,-1};
        int end_position = binarySearch_end(nums,target);
        return vector<int> {begin_position,end_position};
    }
};

也可以写成:

int lower_bound(vector<int> &nums, int target) {
int l = 0, r = nums.size(), mid;
while (l < r) {
mid = (l + r) / 2;
    if (nums[mid] >= target) {
        r = mid;
        } else {
        l = mid + 1;
        } }
    return l;
}
// 辅函数
int upper_bound(vector<int> &nums, int target) {
int l = 0, r = nums.size(), mid;
while (l < r) {
    mid = (l + r) / 2;
    if (nums[mid] > target) {
    	r = mid;
    } else {
    	l = mid + 1;
	}	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值