(坚持每天都写算法)算法基础复习part1基础算法1-4——二分

发布时间 2024-01-09 21:49:09作者: 程序计算机人

  二分使用的前提是有序性的条件如果要找以下情况:

    1.找大于等于数的第一个位置

    2.找小于等于数的第一个位置

  二分使用的前提是无序性的条件下如果要找以下情况:

    1.找最大值

    2.找最小值

  二分法一般有边界问题,如果是有序性的条件下的话只要记住一句话:有加必有减。

  这里是示例代码:

int mid = l + r + 1 (加)>> 1;
if (check(mid)) l = mid;
else r = mid - 1  (减);

  然后这里是另一例代码:

int mid = l + r >> 1;
if (check(mid)) r = mid; 
else l = mid + 1; 

  第一个代码命名为SR,第二个代码名为SL,如果是情况1,那么就用SR,如果是情况2,那么就使用SL。

  二分法找最值,运用到了递归,使用递归分割数组获得某两数组(只有一个元素也可以算作数组,只不过代码是if条件)的最值,比较得出较大的值,作为该两个数组合体后的数组的最值,一直这样下去直到合成的数组是题目给出的数组。

  这里是代码:

int process(int* a,int L,int R){
    //L是该段数据的左边界
    //R是该段数据的右边界
    if(L == R)
       return *(a+L);
    int mid = L+((R-L)/2);//计算中点
    int Leftmax =  process(a,L,mid);
    int Rightmax = process(a,mid+1,R);
    return (Leftmax>Rightmax)?Leftmax:Rightmax;
}

 (如果有点看不懂代码可以思考以下二叉树的左右上排序,递归都是可以画递归树的,画一下就行了)

  这里我搬以下别人的图解,很清晰的。

   

   其实今天想记录的是一道题,使用的二分,顺便把二分写一下。

   这里是代码:

  

//寻找范围,直接使用查找
//check(mid)是查找条件,一定要知道查找条件
//本人习惯
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 100010;

int a[N];
int left_bound(int l , int r , int x){
    //l和r相等了就不用再找了
    while(l < r){
        int mid = l + r >> 1;//左边界,如果r=l + 1 且条件为真,那么更新后可以变成[r,r]  , 靠近左边
        if(a[mid] >= x) r = mid;//因为都大于等于了,即证明还没有到目标值,所以直接等于mid就行了
        else l = mid + 1;//一步步缩减范围,r会凑近左边,如果中间数小于x,那么l就会尝试去mid的右边(可能等于,也可能正好大于,大于的话就要输出-1-1了
    }
    return l;
}

int right_bound(int l , int r , int x){
    while(l < r){
        int mid = l + r + 1 >> 1;//右边界, 如果 r =  l + 1 ,且条件为真,靠近右边
        if(a[mid] <= x ) l = mid;//倒过来了,原理和上面的一样
        else r = mid - 1;//因为是有边界嘛,所以要-1,好凑近右边界,得到目标值
    }
    
    return r;//这里肯定是r,其实l也可以,但是按照逻辑来看还是r好
}
int main(){
    int n , q;
    cin >> n >> q;
    for(int i = 0 ; i < n ; i ++) cin >>a[i];
    
    while(q --){
        int k ;
        cin >>k;
        int begin = left_bound(0 , n -1 , k);
        if(a[begin] != k) cout << "-1 -1" << endl;
        else cout << begin << " " << right_bound(0 , n - 1 , k) << endl;
        //endl是空行符号,中间的“ ” 是空格
    }
    
    return 0;
}

  时间复杂度:每一次都找中间,2^k = n ,所以k = log2_N(就算加了span元素那个N还是在下一行,使用markdown的公式也没法显示,所以就直接这么显示了),即时间复杂度是O(logn)。

  参考链接:AcWing 789. 图解y总的二分模板(最容易理解版本 ) - AcWing

       图文详解递归:以二分法求数组中的最大值为例_二分搜索最大值-CSDN博客