二分搜索专题(1)

先附个经典二分搜索代码

int BinarySearch(int arr[],int N,int target)
{
    int p=0;
    int r=N-1;
    while(p<=r){
        int q=r-(r-p)/2;
        if(arr[q]==target) return q;
        else if(arr[q]<target) p=q+1;
        else r=q;
    }
    return -1;
}

1.考察要点

1.二分搜索不仅在有序序列中可以运用,无序序列也同样适用只要满足每次查找都会缩小一半查找范围

2.注意循环初始条件,边界值,划分点的处理,确保循环不会无法终止

3.划分点的经典写法:(p+r)/2 ,安全写法:r-(r-p)/2或p+(r-p)/2

2.局部最小值问题

  问题描述:在一个无序序列中,且任意两个相邻元素都不相等的情况下,查找满足arr[i]<arr[i+1]且arr[i]<arr[i-1]的元素。

  查找方法:

    1.arr为空或长度为1时返回。

    2.判断两端点的情况

      如果端点值中有一个端点是局部最小值,则立即返回。

    3.端点两边都不是局部最小,如下,讨论中间点mid。

      

    4.中间点的情况

      1.当属于情况2时,直接返回mid.

      2.否则,如3的两边,4的左边,比存在局部最小值,因为相邻元素不同,以4的左边为例,

      在mid的左边,一旦出现上升点,则此点局部最小,否则若一直向左降序,则arr[1]为局部最小值。

      3.未画出的情况5参考情况4。

      

附一个查找局部最小元素的代码

int BinarySearch2(int arr[],int p,int r)
{
    //if(p>r) return -1;
    if(p==r) return p;
    if(arr[p]<arr[p+1]) return p;
    if(arr[r]<arr[r-1]) return r;
    int q=r-(r-p)/2;
    if(arr[q]<arr[q-1]){
        if(arr[q]<arr[q+1]) return q;
        return BinarySearch2(arr,q+1,r);
    } else{
        return BinarySearch2(arr,p,q-1);
    }
}

 3.含有重复元素的有序数组问题

   问题描述:给定有序数组arr,num,查找num在arr中出现的最左的位置。

   举例分析:num=3,arr={1,2,3,3,4,4,4,4,4}

       1.初始状态:res=-1,arr={1,2,3,3,4,4,4,4,4}

       2.mid=4,arr={1,2,3,3},res=-1;

       3.mid=2,arr={3,3},res=-1;

       4.mid=3左,arr={},返回res=2.

   总结:维护一个res,当找到num时,继续向左找,直到要找部分为空。

附一个查找最右元素的代码  

int BinarySearch1(int arr[],int p,int r,int target,int res)
{
    if(p>r) return res;
    int q=(p+r)/2;
    if(arr[q]==target) return BinarySearch1(arr,q+1,r,target,q);
    else if(arr[q]<target) return BinarySearch1(arr,q+1,r,target,res);
    else return BinarySearch1(arr,p,q-1,target,res);
}

4.求有序循环序列的最小元素

 对于数组L.....R,如{4,5,6,0,1,2,2,3}

 1.如果arr[L]<arr[R],则返回arr[L]

 2.如果arr[M]>arr[R],则最小值位于M...R

 3.如果arr[M]<arr[L],则最小值位于L...R

 4.如果arr[L]<=arr[M]<=arr[R],又arr[L]>=arr[R],故arr[L]=arr[R]=arr[M],此时只能遍历求得最小值。

 5.由于划分元素不会被裁去,需要重新处理边界值问题(此题当元素个数为2时继续循环不会减少)。

附一个查找最小元素的代码

int BinarySearch3(int arr[],int N)
{
    int p=0;
    int r=N-1;
    while(1){
         if(r-p == 1) return arr[r];
        if(arr[p]<arr[r]) return arr[p];
        int q=r-(r-p)/2;
        if(arr[q]<arr[p]) r=q;
        else if(arr[q]>arr[r]) p=q;
        else{
            int m=arr[p];
            for(int i=p+1;i<=r;i++) m=m<arr[i]?m:arr[i];
            return m;
        }
    }
        return -1;
}

5.求有序无重复元素的最左原位

1.定义:最左原位:满足arr[i]=i的最左位置i

2.步骤:

    初始化res=-1

  1.如果arr[0]>N-1或arr[N-1]<0,返回-1

  2.如果arr[M]<M,则左边排除,搜索0...M-1(这是因为数组有序且不重复,故左半部分最低与i减少速度相同,故arr[i]恒小于i

     如果arr[M]>M,则右边排除,搜索M+1...N-1

  3.如果arr[M]=M,更新res,由于找最左原位,故继续向左找

  4.重复1-3,直到待查找序列空。

附一个查找最左原位

int BinarySearch4(int arr[],int N)
{
    int res=-1;
    int p=0;
    int r=N-1;
    while(p<=r){
        int q=r-(r-p)/2;
        if(arr[p]>r) return res;
        if(arr[r]<p) return res;
        else if(arr[q]>q) r=q-1;
        else if(arr[q]<q) p=q+1;
        else {
            res=q;
            r=q-1;
        }
    }
    return res;
}
原文地址:https://www.cnblogs.com/lshao/p/8970563.html