二分查找与二分答案

目录

前言引入

二分查找

整数域的二分

递归式

非递归式

分区段二分

实数域的二分

已知精度

未知精度

二分答案


前言引入

(听说,只有10%的程序员能一次性写对二分喔)

当有一大堆的数据,需要我们在这么一大堆的数据中,找到我们想要的那个数据。毫无疑问,打个循环,慢慢开始遍历,确实可以找到,但是这样耗费的时间就太长了。

因此就诞生了二分查找。甚至还衍生出了二分答案。

同时,根据二分的原理,还可以衍生到三分甚至n分。

以及实数域上的二分(例如一元三次函数的求解)

二分查找

整数域的二分

二分的原理:

基于高中数学的零点问题,在一个有序区间【left,right】内,要查找某个数,就看要查找的这个数,和这个区间最中间的数进行比较,如果要查找的数大于中间数,那么接下来,就在区间【mid+1,right】中进行查找;反之,就查找区间【left,mid】。依此类推。

例如10个数据 1,2,3,4,5,6,7,8,9,10。

要查找的key:7

首先找到最中间的数:(left+right)/2 可得知为5。

然后,将5与key(7)比较,发现7>5。因此,下一步就在区间【6,10】中进行查找。

找到最中间的数:8。

然后,将8与key(7)比较,发现7<8。因此,下一步就在区间【6,8】中进行查找。

找到最中间的数:7。

将7与key(7)比较,欸,刚好等于key,所以接下来就退出查找,因为已经找到啦!

递归式

int BinarySearch(int left,int right,int key){
    if(left==right) {
        if(a[left]==key) return left;
        else return -1;
    }
    int mid=(left+right)>>1;//位运算和除2一样
    if(a[mid]>=key) return BinarySearch(left,mid,key);
    else return BinarySearch(mid+1,right,key);
}

非递归式

int BinarySearch(int left,int right,int key){
    while(left<right) {
        int mid=(left+right)>>1;//位运算和除2一样
        if(a[mid]>=key) right=mid;
        else left=mid+1;
    }
    if(a[left]==key) return left;
    else return -1;
}

从测评可以看出,非递归式的空间复杂度要比递归式小很多

当然,以上代码只局限于在单调递增序列中查找>=key的第一个数据

例如1 2 2 2 3 4 5;key==2,那么找到的位置即是

          ^

要想找到最后一个2,则模板改为:

int BinarySearch(int left,int right,int key) {
    while(left<right) {
        int mid=(left+right+1)>>1;
        if(a[mid]<=key) left=mid;
        else right=mid-1;
    }
    if(a[left]==key) return left;
    else return -1;
}

分区段二分

假设数据量过大,要我们查找【1,1e18】之间的某个数,而恰好,要找的数又是极端情况。

难免时间也会过长。这个时候可以分区段二分。

例如,可以分成100段,第一段就是【1,1e16】,第二段就是【1e16,2e16】.....

然后我们查找1e16是否满足, 若满足,则在【1,1e16】进行二分或者又一次分段查找。

若不满足,则查找2e16是否满足。

分区段二分的上限很高,分的度不好把控,也就是说根据分的区段的不同,算法时间也会有差异。

分的区段过多,相当于遍历了,并非二分,分的过少,极端情况二分也不合适。

所以最优解很难求出,需得大量的实践。然而,换个数据,最优解又不同了。

实数域的二分

实数域二分典型的例子就是一元三次函数求解。当然,也运用了一下分区段二分的思想。

已知精度

通常,实数域的二分,在已知精度的情况下,为了避免误差,会继续保持精度在后两位进行查找。

例如 :已知精度为0.001,实际上查找是在0.00001的精度,也就是1e-5

double BinarySearch(double left,double right){
    while(left+eps<right) { //eps为精度
        double mid=(left+right)/2;
        if(check(mid)) right=mid;//check为与查找条件
        else left=mid;
    }
    if(check(left)) return left;//check为结束条件
    else return -1;
}

未知精度

精度不容易确定,就采用循环固定次数的方式。固定查找n次,总有一次能找到我们满意的值。

double BinarySearch(double left,double right){
    for(int i=1;i<=n;i++) {
        double mid=(left+right)/2;
        if(check(mid)) right=mid;
        else left=mid;
    }
    if(check(left)) return left;//check为结束条件
    else return -1;
}

以下为洛谷一元三次函数求解的题 

https://www.luogu.com.cn/problem/P1024

以下就是我ac的代码(不是最优,可供参考)

#include<iostream>
#include<algorithm>
#include <cstdio>
using namespace std;
double a,b,c,d;
double f(double x) {
    return a*x*x*x+b*x*x+c*x+d;
}
double BinarySearch(double left,double right) {
    //若是单调递减
    if(f(left)>0) {
        while(left+1e-3<right) {
            double mid=(left+right)/2;
            if(f(mid)<-0.0001) right=mid;
            else left=mid;
        }
    }
    //若单调递增
    else {
        while(left+1e-3<right) {
            double mid=(left+right)/2;
            if(f(mid)>0.0001) right=mid;
            else left=mid;
        }
    }
    return left;
}
int main(){
    cin>>a>>b>>c>>d;
    for(double i=-100;i<=100;i++) {
        if(f(i)>=-0.0001&&f(i)<=0.0001) printf("%.2lf ",i);
        else if(f(i)*f(i+1)<0) {
            printf("%.2lf ",BinarySearch(i,i+1));
            i++;//这里i++防止重复
        }
    }
    cout<<endl;
    return 0;
}

二分答案

二分答案问题,其原理可对应高中学的函数,在定义域x∈【a,b】内,要求是单调的,并且每一个x都有其对应的y∈值域【c,d】。x增加,那么y的值也会对应增加或减少。

二分答案问题即:在题目限制的y的条件下(y大于某个数或小于某个数),找到最优的x。 

但是,从最左边开始遍历查找,时间难免过长。因而采用二分查找的方式,这就是二分答案算法。

下面以一次函数 y=kx+b为例。

题目已知要求:y<=c。要我们在定义域【m,n】内找到一个数,让其对应的y达到要求 。

由图可知,x∈【m,a】都是满足的,因此最优解就是a,所以,我们在区间【m,n】中采用二分查找的方法,就能找到这个a

以下就是解决这个问题的模板代码

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int k,b,y;//k,b为题目已知条件
//啥?你说题目没明给?那自己推条件呀
//y为题目要求输入的值
int f(int x) {
    return k*x+b;
}
bool check(int key) {
    if(f(key)>=y) return true;
    else return false;
}
int main() {
    int left,right;//left,right为题目已知条件
    //啥?你说题目没明给?那自己推条件呀
    //这里的left,right就是一次函数的定义域m,n。
    cin>>y;
    while(left<right) {
        int mid=(left+right)>>1;
        if(check(mid)) right=mid;
        else left=mid+1;
    }//check(mid)即为判断条件
    cout<<left<<endl;
    system("pause");
    return 0;
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
二分查找二分答案是两个不同的概念。 二分查找(Binary Search)是一种常用的查找算法,适用于有序的数组或列表。它通过将目标值与数组中间元素进行比较,从而确定目标值在数组的左半部分还是右半部分,然后再在相应的半部分中继续查找,直到找到目标值或者确定目标值不存在。二分查找的时间复杂度是 O(logn)。 以下是一个简单的 C++ 实现示例: ```cpp int binarySearch(int arr[], int target, int left, int right) { while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) return mid; else if (arr[mid] < target) left = mid + 1; else right = mid - 1; } return -1; // 目标值不存在 } ``` 二分答案(Binary Search for Answer)是一种在某个范围内寻找满足某个条件的最优解的方法。它通过在一个给定范围内进行二分搜索,每次确定一个中间值,然后判断该中间值是否满足条件。如果满足条件,则将搜索范围缩小到左半部分;如果不满足条件,则将搜索范围缩小到右半部分。通过不断缩小搜索范围,最终找到满足条件的最优解。 这种方法在一些问题中很常见,比如在一个有序数组中找到满足某个条件的最小/最大值,或者确定一个函数的最大/最小值等。 具体的实现方式会根据具体的问题而有所不同,但基本的思路是相似的。你可以根据具体的问题来实现相应的二分答案算法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

青衿白首志

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

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

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

打赏作者

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

抵扣说明:

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

余额充值