详解二分法查找目标值/二分法查找上下界的两种写法

文章讨论了在整数数组中使用二分查找算法时,针对不同查找需求(等于、小于等于、大于等于)对左边界和右边界更新的优化策略,强调了在升序区间查找≤时应采用(l≤r)写法以避免错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文中默认数组呈升序。

💡 绝对不能使用left = cur这种更新,由于整数除法取下界,left不更新会导致循环无法结束

寻找与target相等

  1. 可以用(l < r)写法,末尾r==l需要检查是否满足条件,满足则输出,不满足则输出-1
  2. 可以用(l<=r) 写法,末尾输出r 不需要检查是否满足条件,不满足的情况下r为-1,因此结果r可能会超出数组索引,注意不要对此进行nums[r]的检查
// ==
// Method1 : left=mid+1, right=mid, 此时最优解为最后的重合解(若可行)
// 遍历直至left = right,左右重合时退出循环,此时重合解若可行,即为答案
int BinSearch1(vector<int> &nums, int target) {
    int l = 0;
    int r = nums.size()-1;
    int mid;
    while (l < r) {
        mid = (l+r)/2;
        if (nums[mid] == target)
            return mid;
        else if (nums[mid] < target) {
            l = mid + 1;
        }
        else
            r = mid;
    }
    if (nums[r] == target)
        return r;
    return -1;
}

// ==
// Method2 : left=mid+1, right=mid-1, 此时最优解需要另外记录
// 遍历直至left > right,左右重合时仍然进入循环并进行结果记录
int BinSearch2(vector<int> &nums, int target) {
    int l = 0;
    int r = nums.size()-1;
    int mid;
    while (l <= r) {
        mid = (l+r)/2;
        if (nums[mid] == target)
            return mid;
        else if (nums[mid] < target) {
            l = mid + 1;
        }
        else
            r = mid - 1;
    }
    return r;
}

寻找最后一个小于(等于)target

💡 需要使用(l<=r) 的写法

因为查找≤时不满足条件更新的是右界,满足条件下更新左界;

如果采用(l<r)写法:

  • 更新左界如果使用l = mid+1 ,由于mid此时满足条件,会导致新区间错失满足条件的点,答案错误
  • 如果使用l=mid,由于mid=(l+r)/2整数除法取下界,某些情况下左界会不更新,从而引起循环无法结束,程序错误

综上,在升序区间查找≤时只能采用(l≤r) 的写法,并通过r输出最终结果:若无满足条件的元素,将会使得r==-1

// <
int BinLBound(vector<int> &nums, int target) {
    int l = 0;
    int r = nums.size()-1;
    int mid;
    while (l <= r) {
        mid = (l+r)/2;
        // 先更新不满足条件
        if (nums[mid] >= target) {
            r = mid - 1;
        }
        else
            l = mid + 1;
    }
    return r;
}

// <=
int BinLEBound(vector<int> nums, int target) {
    int l = 0;
    int r = nums.size()-1;
    int mid;
    while (l <= r) {
        mid = (l+r)/2;
        if (nums[mid] > target)
            r = mid - 1;
        else
            l = mid + 1;
    }
    return r;
}

寻找第一个大于(等于)target

💡 需要使用(l<r) 的写法

// >
int BinHBound(vector<int> &nums, int target) {
    int l = 0;
    int r = nums.size()-1;
    int mid;
    while (l < r) {
        mid = (l+r)/2;
        if (nums[mid] <= target)
            l = mid + 1;
        else
            r = mid;
    }
    if (nums[r] > target)
        return r;
    return -1;
}
// >=
int BinHEBound(vector<int> &nums, int target) {
    int l = 0;
    int r = nums.size()-1;
    int mid;
    while (l < r) {
        mid = (l+r)/2;
        if (nums[mid] < target)
            l = mid + 1;
        else
            r = mid;
    }
    if (nums[r] >= target)
        return r;
    return -1;
}

整体测试代码

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> nums{-5, 0, 1, 2, 5, 10, 30, 90};
    int target = 90;
    cout << BinSearch1(nums, target) << " " << BinSearch2(nums, target) << endl;
    cout << BinLBound(nums, target)  << " " << BinLEBound(nums, target) << endl;
    cout << BinHBound(nums, target)  << " " << BinHEBound(nums, target);
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值