二分查找&二分答案

二分需要满足两个条件:

(1)数组存储
(2)元素有序,单调性

时间复杂度:O( l o g 2 n log_2^n log2n

整数二分算法模板

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

Tips:1. 用模板 1 还是模板 2 可以先判断 if(check(mid)) 满足时:

​ ① r = mid——模板1 (mid = l + r >> 1)

​ ③ l = mid——模板2 (mid = l + r +1 >> 1)

  1. 当循环while( l < r ) 结束时,l =r

浮点数二分算法模板

bool check(double x) {/* ... */} // 检查x是否满足某种性质

double bsearch_3(double l, double r)
{
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

Tips:
题目要求保留:①四位小数——eps = 1e-6

​ ②五位小数——eps = 1e-7

​ ③六位小数——eps = 1e-8

(while ( r - 1 > eps ) 可用100次for循环来代替,当 r - l 足够小时认为找到了答案)

什么是二分答案?

答案属于一个区间,当这个区间很大时,暴力超时。但重要的是——这个区间是对题目中的某个量有单调性的,此时,我们就会二分答案。每一次二分会做一次判断,看是否对应的那个量达到了需要的大小。相当于把答案从它可能所在的区间里二分出来。
判断:根据题意写个check函数,如果满足check,就放弃右半区间(或左半区间),如果不满足,就放弃左半区间(或右半区间)。一直往复,直至到最终的答案。

如何判断一个题是不是用二分答案做的呢?

1、答案在题目给的一个区间内(一般情况下,区间会很大,暴力超时)
2、直接搜索不好搜,但是容易判断一个答案可行不可行
3、该区间对题目具有单调性,即:在区间中的值越大或越小,题目中的某个量对应增加或减少。
此外,可能还会有一个典型的特征:求…最大值的最小 、 求…最小值的最大

补充:STL中的二分low_bound和upper_bound
lower_bound()用于在已排好序的数组中找出大于等于/小于等于 目标元素的下标最小的元素的地址。
upper_bound()用于在已排好序的数组中找出大于/小于目标元素的下标最小的元素的地址。

(返回迭代器) lower_bound (开始位置,结束位置,查找元素);
(返回迭代器) upper_bound(开始位置,结束位置,查找元素);
这两个函数都有三(四)个参数,第四个参数是被查找函数的排序方式,如果不填就默认为从小到大排序。
第一个参数是被查找数组的首地址,第二个参数是末地址,第三个是要目标元素,第四个是被查数组的排序方式。
被查数组排序方式写的时候返回值应为bool类型,设定两个参数(与被查数组内元素类型相同即可)。

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

int a[N];
int cmd(int a, int b)
{
    return a > b;
}

int main()
{
    int a[7] = {1, 2, 3, 7, 7, 9,15};
    sort(a, a+7);
    int pos1 = lower_bound(a, a+7, 7) - a;
    int pos2 = upper_bound(a, a+7, 15) - a;
    cout << pos1 << " " << pos2 << endl;

    sort(a, a+7, cmd);
    int pos3 = lower_bound(a, a+7, 2, greater<int>()) - a;
    int pos4 = upper_bound(a, a+7, 1, greater<int>()) - a;
    cout << pos3 << " " << pos4 << endl;
    return 0;
}

//输出:
3 7
5 7
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zlq070707

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值