【二分查找】二分查找的三种实现方式以及死循环出现原因

二分的终止条件

  此文章是观看BV1AP41137w7写出来的,建议没明白二分的都去看看

  众所周知,二分查找是利用数据有序的特点实现的一种高效查找的方式,二分的终止条件是区别二分查找方式的重要条件。以下约定,使用蓝色表示小于x的所有数,红色表示大于等于x的所有数,数组为a[]。而l,r的最终条件和中间态则会清晰表示出来。

以下三种二分终止条件:

    闭区间查询: l,r指针总是默认在闭区间查找。

    

 

    我们先默认将l置于数组首位置,r置于数组尾,容易发现此时答案在区间[l,r]中 取 mid=l+(r - l)/2。//c++中有可能发生爆内存的悲剧 。 

    mid为6 并且a[6] = 7 < 8 那么我们先将l = mid + 1,以下会说明原因。那么此时图会变成:

    

     还记得吗?蓝色表示小于x的区间接着更新mid = 9 并且a[mid] > 9 同样,将r设置为mid - 1那么:

    

     我们一直进行到最终状态:

    

     这就是最终状态,因为每次更新时L = mid + 1(a [mid] < x)那么L具有这样的性质

    1,L右边的数都 < x 那么L就是第一个>= x  的数

    同样R具有类似性质

    1,R右边的数都 >= x

    而二分查找中L,R的性质不会改变,在保证这样的条件下,闭区间[L,R]中查找会得到上图

   闭区间代码:

      

#include<bits/stdc++.h>
const int INF = 1e9 + 10;
using namespace std;

int main (){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    int n;
    cin>>n;
    vector<int> a(n + 1);
    for(int i=1; i<=n; i++){
        cin>>a[i];
    }
    int target;
    cin>>target;
    int l = 1,r = n;
    //根据l,r的性质,当l == r 时,不满足l左边都 < x ,r右边都 >= x
    //那么l == r 时不是最终状态
    while(l <= r){
        int mid = l + (r - l) / 2;
        //在c++中int最大为1e9左右,当两个最大int数相加,会造成溢出
        //故写成等价表达式mid = l + (r - l) / 2
        if(a[mid] < target) l = mid + 1;
        else r = mid - 1;
        //else 表示当a[mid] >= target的情况 
    }
    //根据l,r的性质最终第一个 >= x 的数是a[l]
    if(a[l] == target) cout<<"Yes"<<'\n';
    else cout<<"No"<<'\n';
    return 0;
}

 

    左闭右开区间:

    初始状态

     最终状态

 

 

    另外贴出左闭右开区间得最终结果图和L,R性质,

     左闭右开区间代码

#include<bits/stdc++.h>
const int INF = 1e9 + 10;
using namespace std;

int main (){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    int n;
    cin>>n;
    vector<int> a(n + 1);
    for(int i=1; i<=n; i++){
        cin>>a[i];
    }
    int target;
    cin>>target;
    int l = 1,r = n + 1;
    //注意此时是在左闭右开区间查询[l,r)
    //l性质:l(不包含l)左边都 < x
    //r性质:r(包含r)右边都 >= x
    //l == r 时区间[l,r) 没有数退出
    while(l < r){ 
        int mid = l + (r - l) / 2;
        if(a[mid] < target) l = mid + 1;
        else r = mid;
    }
    //根据l,r的性质最终第一个 >= x 的数是a[l]或a[r]
    if(a[l] == target) cout<<"Yes"<<'\n';
    else cout<<"No"<<'\n';
    return 0;
}

 

    同样的开区间代码和开区间图

    

     

     

#include<bits/stdc++.h>
const int INF = 1e9 + 10;
using namespace std;

int main (){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    int n;
    cin>>n;
    vector<int> a(n + 1);
    for(int i=1; i<=n; i++){
        cin>>a[i];
    }
    int target;
    cin>>target;
    int l = 0,r = n + 1;
    //此时是在开区间查询(l,r)
    //l性质:l(包含l)左边都 < x
    //r性质:r(包含r)右边都 >= x
    //l == r 时区间[l,r) 没有数退出
    while(l + 1 != r){ 
        int mid = l + (r - l) / 2;
        if(a[mid] < target) l = mid;
        else r = mid;
    }
    //根据l,r的性质最终第一个 >= x 的数是a[r]
    if(a[r] == target) cout<<"Yes"<<'\n';
    else cout<<"No"<<'\n';
    return 0;
}

  二分会陷入死循环的原因就是由于未弄清楚l,r性质以及最终状态导致只要明白原理就没有大问题了

posted @ 2023-12-04 21:49  意外路过的番茄酱骑士  阅读(295)  评论(0)    收藏  举报