二分搜索问题总结

70 篇文章 0 订阅
37 篇文章 0 订阅

最近工作中遇到了二分搜索问题。它是计算机课程中最基础的算法,这是我们在顺序查找时,最直接的一种提速的普遍性算法了,能够将一个顺序的查找的时间复杂度,从O(n)提升到了O(logn),这是质变的算法。

首先我们来看一下一个经典的二分搜索算法:

int BinarySearch(vector<int>& vec_arr, int target) {
    int left = 0;
    int right = vec_arr.length() - 1;
    while(left <= right) {
        //这里之所以不直接left + right,是因为存在溢出的可能,所以实现的时候需要规避这个问题
        int mid = left + (right - left) / 2;
        if (vec_arr[mid] == target) {
            return mid;
        }
        //目标在mid的右边,修改左边界
        else if (target > vec_arr[mid]) {
            left = mid + 1;
        }
        //目标在mid的左边,修改右边界
        else {
            right = mid - 1;
        }
    }

    return -1;
}

这个算法不用多说,就是一个有序序列中查找一个目标值的位置信息,是最经典的一个场景。

下面总结几个可能的二分变种使用场景。

1)如果目标值有多个,找出第一个出现的值,即是左边界,直接阅读源码,看注释就明白 

int BSFindFirstEqual(const vector<int>& vec_arr, int target) {
    int left = 0;
    int right = vec_arr.size() - 1;
    //循环继续条件,这里必须是<=
    while (left <= right) {
        int mid = left + (right - left) / 2;
        //这里需要重点关注的是,如果vec_arr[mid] == target的时候,由于需要寻找左边界,所以需要让right往左靠,所以满足相等的时候,执行right = mid - 1;
        if (target <= vec_arr[mid]) {
            right = mid - 1;
        }
        else {
            left = mid + 1;

        }
    }
    
    //最后,如果可能找到目标,那就只可能是left位置,做下判断
    if (vec_arr[left] == target && left < vec_arr.size()) {
        return left;
    }

    return -1;
}

2)如果目标值有多个,找出最后一个出现的值,即是右边界,直接阅读源码,看注释就明白

int BSFindLastEqual(const vector<int>& vec_arr, int target) {
    int left = 0;
    int right = vec_arr.size() - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        //情况与问题1类似,当vec_arr[mid]与target相等时,我们需要找右边界,那需要让left往右移动
        if (target >= vec_arr[mid]) {
            left = mid + 1;
        }
        else {
            right = mid - 1;
        }
    }
    
    //最后,如果可能找到目标,那就只可能是left位置,做下判断
    if (vec_arr[right] == target && right >= 0) {
        return right;
    }

    return -1;
}

3)找到第一个大于等于target的数的位置

//这个核心算法,与找第一个与target相等的一样。区别在于,当找不到的时候,需要返回第一个比target大的数,即是left左指针
int BSFindFirstEqualLarger(const vector<int>& vec_arr, int target) {
    int left = 0;
    int right = vec_arr.size() - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        //当vec_arr[mid] == target的时候,right往左靠
        if (target <= vec_arr[mid]) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    
    return left;
}

4)找第一个大于target的位置

int BSFindFirstLarger(const vector<int>& vec_arr, int target) {
    int left = 0;
    int right = vec_arr.size() - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (target >= vec_arr[mid]) {
            left = mid + 1;
        }
        else {
            right = mid - 1;
        }
    }
    
    return left;
}

 5)找最后一个小于等于target的值

int BSFindLastEqualSmaller(const vector<int>& vec_arr, int target) {
    int left = 0;
    int right = vec_arr.size() - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (target >= vec_arr[mid]) {
            left = mid + 1;
        }
        else {
            right = mid - 1;
        }
    }
    
    return right;
}

6)找最后一个小于target的值

int BSFindLastSmaller(const vector<int>& vec_arr, int target) {
    int left = 0;
    int right = vec_arr.size() - 1;
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        
        if (target <= vec_arr[mid]) {
            right = mid - 1;
        }
        else {
            left = mid + 1;
        }
    }
    
    return right;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值