二分查找学习

数组在有序的情况查找某元素,用二分查找可以达到logn的时间复杂度。二分查找虽然看似容易,想要把这个算法完全写好,并灵活运用确并非易事儿。据说专业的程序员有90%的人无法正确实现。如果你不信,不妨自己动手试一试,然后用一些测试用例测一下。

二分查找的思想:在有序数组A[n]中查找x,令s=0,e=n-1,我们先相信x在[s,e]区间,m=(s+e)/2为中间坐标,如果正好x=A[m]则找到输出;如果x<A[m],则x出现m的左边,反之出现在m的右边。这样每次都可以缩小查找范围,当s>e时未找到则没出现,返回-1。

根据思想不难写出以下代码:

int binarySearch(int A[],int n,int x)
{
    int s = 0;
    int e = n-1;
    while(s<=e)
    {
        int mid = (s+e)>>1;
        if(A[mid]<x)
            s = mid + 1;
        else if(A[mid]>x)
            e = mid - 1;
        else
            return mid;
    }
    return -1;
}

注:上述代码如担心s+e溢出的话,可以采用 mid = s + (e-s)>>1;

上述实现较容易,且不容易出错。另外的几种变体使用起来就要小心了。

变体1:

int binarySearch1(int A[],int n,int x)
{
    int s = 0;
    int e = n;
    while(s<e)
    {
        int mid = (s+e)>>1;
        if(A[mid]<x)
            s = mid + 1;
        else if(A[mid]>x)
            e = mid;
        else
            return mid;
    }
    return -1;
}

这种方法采用的是右边开区间的边界(不包含)。那么注意当A[mid]>x时,修改结束坐标e时,同样的要不包含右边界,即e=mid。

变体2:

如果数组中有多个满足条件的元素,在算法1中我们随机的找出了一个位置。在《编程珠玑》中给出了一种方法,可以找到第一个满足条件的位置

//找出第一个满足条件的元素x
int getFirst(int A[],int n,int x)
{
    int s = -1;
    int e = n;
    while(s+1!=e)  //s<e && A[s]<x<=A[e]
    {
        int mid = (s+e)>>1;
        if(A[mid]<x)
            s = mid;
        else
            e = mid;
    }
    if(e>=n || A[e]!=x)
        e = -1;
    return e;
}

这种方法可能不太好理解,至少我第一读到的时候就纳闷为什么要这么写呢(没办法,脑袋转的慢呀!-!)。后来想了想终于想通了:因为是要找第一个满足的,那我们在某次判断时,无论当前的是不是要找的都要缩小范围是吧,关键是在找到了“它“,把它分到哪部分好呢?肯定是右半部份啦,因为这样他就变成火车尾了。一段时间后回头再看,上句话说的不清楚,下面给出更简单的解释。当找到这个元素,即mid位置为x时,如何修改下标呢?因为不能排除mid前面还有没有x了,所以动e,令e=mid。

同理,我们可以得出找到最后一个位置的方法:

//找出最后一个满足条件的元素x位置
int getLast(int A[],int n,int x)
{
    int s = -1;
    int e = n;
    while(s+1!=e)  //s<e && A[s]<=x<A[e]
    {
        int mid = (s+e)>>1;
        if(A[mid]>x)
            e = mid;
        else
            s = mid;
    }
    if(s<=0 || A[s]!=x)
        s = -1;
    return s;
}

另外还有其他各种变种,如找到第一个大于给定元素的位置,找出第一个小于给定元素的位置,找出第一个大于等于给定元素的位置等等,看起来都差不多,写起来还真没那么容易,今天写到这里,后面几个随后再补充吧。也欢迎小伙伴们在评论中指正和补充~

 

posted @ 2014-10-31 20:46  smurf  阅读(177)  评论(0编辑  收藏  举报