【C++】【查找算法】二分查找;upper 、lower bound;floor、ceil 等变种问题。【二分总结】


一、二分查找-basic

时间复杂度 :O(logn);
要求:排序数组。

1. caution:

循环不变量的定义 :// 在arr[l…r]之中查找target
循环继续条件: l <= r ,因为循环不变量定义了是闭区间,所以要加上等号,使得相等的时候还继续循环。

// 二分查找法,在有序数组arr中,查找target
// 如果找到target,返回相应的索引index
// 如果没有找到target,返回-1
template<typename T>
int binarySearch(T arr[], int n, T target){

    // 在arr[l...r]之中查找target
    int l = 0, r = n-1;
    while( l <= r ){

        //int mid = (l + r)/2;
        // 防止极端情况下的整形溢出,使用下面的逻辑求出mid
        int mid = l + (r-l)/2;

        if( arr[mid] == target )
            return mid;

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

    return -1;
}

二、upper、lower bound

upper(找上界):找到大于target 的最小值。

lower (找下界):找到大于等于target 的最小值。

如果数组中没有target,那么upper和lower 返回相同索引。

有的地方定义不同,反正代码跟着定义走就行,学会了的话,怎样变种都能做。

1. caution:

循环不变量的定义 :// 在arr[l…r]之中寻找解

循环继续条件: l < r //结束时,l==r ,随便返回一个

上边界初始化: r = length; //上边界也有可能是解(target大于所有值,返回溢出的位置)

右边界更新 : r = mid; //因为解有可能是mid。

2.upper bound

    template<typename T>
    int upper_bound(T arr[], int n, T target){
        int l = 0, r = n;
        while(l < r){
            int mid = l + (r - l) / 2;
            if(arr[mid] <= target)
                l = mid + 1;
            else    // nums[mid] > target
                r = mid;
        }
        return l;
    }

3.lower bound

    template<typename T>
    int lower_bound(T arr[], int n, T target){
        int l = 0, r = n;
        while(l < r){
            int mid = l + (r - l) / 2;
            if(arr[mid] < target)
                l = mid + 1;
            else    // nums[mid] >= target
                r = mid;
        }
        return l;
    }

二、ceil、floor

ceil:

  1. 如果数组中存在target,返回target中的最大索引;
  2. 如果不存在target,返回upper(大于这个值的最小元素索引);
  3. 如果target大于所有的值,返回数组个数n(即数组上溢出的索引)

floor:

  1. 如果找到target,返回第一个target 的索引;
  2. 如果没有找到target,返回比target小的最大值的索引;
  3. 如果target小于所有的值,返回-1(即数组下溢出的索引);

1. caution:

  1. 上下边界的选择:floor:-1 ~ n-1;ceil:0 ~ n;

  2. mid 的上下取整:floor :mid = l + (r-l+1)/2; //向上取整避免死循环

(因为有边界每次更新都是小于mid 的,所以让mid向上取整,并不会进入死循环;而其他的都是下边界更新时候是 l = mid+1,所以让mid向下取整没有问题)

  1. 找到与没找到之间的判断和返回值。

2.ceil

template<typename T>
int ceil(T arr[], int n, T target){
    // 寻找比target大的最小索引值
    int l = 0, r = n;
    while( l < r ){
        // 使用普通的向下取整即可避免死循环
        int mid = l + (r-l)/2;
        if( arr[mid] <= target )
            l = mid + 1;
        else // arr[mid] > target
            r = mid;
    }

    assert( l == r );

    // 如果该索引-1就是target本身, 该索引+1即为返回值
    if( r - 1 >= 0 && arr[r-1] == target )
        return r-1;

    // 否则, 该索引即为返回值
    return r;
}

2.floor

template<typename T>
int floor(T arr[], int n, T target){
    // 寻找比target小的最大索引
    int l = -1, r = n-1;
    while( l < r ){
        // 使用向上取整避免死循环
        int mid = l + (r-l+1)/2;
        if( arr[mid] >= target )
            r = mid - 1;
        else
            l = mid;
    }

    assert( l == r );

    // 如果该索引+1就是target本身, 该索引+1即为返回值
    if( l + 1 < n && arr[l+1] == target )
        return l + 1;

    // 否则, 该索引即为返回值
    return l;
}

三、相关问题

875.爱吃香蕉的珂珂

1011 .在 D 天内送达包裹的能力


参考

模板类、模板函数设计

bobo老师的github

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值