二分查找那些事

二分查找可以学习如何上下界问题非常有帮助。

首先讨论统一接口的情况,便于实现其他算法的版本。

这里使用两种思路去讨论算法的实现形式,第一种以“减而治之”的策略实现并从查找行为平衡度等方面对算法进行改进,第二种直接从通过规则的讨论直接讨论算法组成,各层判断。

语义定义:在有序向量的区间[l,h)内查找元素v,返回秩最大者。

语义定义:在有序向量的区间[l,h]内查找元素v,返回秩最大者。

upper_bound 当v存在时返回它出现的最后一个位置的后面一个位置。如果不存在,返回这样一个下标,在此处插入e,原来元素全部往后移动一个位置,后序列仍然有序。

lower_bound 当v存在时返回它出现的第一个位置。如果不存在,返回这样一个下标,在此处插入e,原来元素全部往后移动一个位置,后序列仍然有序。

之所以有如上区别,在于实现时候逻辑的统一性。请看下表

首先讨论上述情况下循环体的三种情况。

设lo为左边界,hi为右边界,mid = (lo+hi)/2,那么,根据mid的取值,分三种情况:

a[mid] == v,中间值等于v,而要求的是最后一次出现的v。最后出现的v值一定在mid的右边或者就是mid位置的这个值,所以我们应该调整左边界,lo = mid,区间变为[mid,hi]

a[mid] < v,说明v如果在数组中,应该出现在mid右侧,则调整左边界,l = mid + 1,区间变为[mid+1,hi]

a[mid] > v,说明v如果在数组中,应该出现在mid左侧,则调整右边界,v如果不在数组中,可能出现在mid的位置上,h=mid,区间变为[lo,mid]

a[mid] == v,中间值等于v,而要求的是最后一次出现的v。最后出现的v值一定在mid的右边或者就是mid位置的这个值,所以我们应该调整左边界,lo = mid,区间变为[mid,hi]

左闭右闭区间,upper_bound

A[m]=v 至少已经找到一个,而右边可能还有,因此区间变为[m,h]

A[m]>v 所求位置不可能在右边,但有可能是m,因此区间变为[l,m]

A[m]<v m和前面都不可行,因此区间变为[m+1,h]

左闭右闭区间,lower_bound

A[m]=v 至少已经找到一个,而左边可能还有,因此区间变为[l,m]

A[m]>v 所求位置不可能在右边,但有可能是m,因此区间变为[l,m]

A[m]<v m和前面都不可行,因此区间变为[m+1,h]

 

左闭右开区间,upper_bound

A[m]=v 至少已经找到一个,而右边可能还有,因此区间变为[m,h)

A[m]>v 所求位置不可能在右边,但有可能是m,因此区间变为[l,m]

A[m]<v m和前面都不可行,因此区间变为[m+1,h]

 

设lower_bound和upper_bound的返回值分别为L和R,则v出现的子序列为[L,R)。这个结论当v不存在的时候也成立:此时L=R,区间为空。

这里实现的就是STL中的同名函数。

三分支版本:

二分查找详解

http://www.cnblogs.com/segeon/archive/2012/07/27/2612361.html 

两分支版本:

 

问题描述:有一个按非降序排列的有序数组a[0...n-1]和一个数v

1. 求数组a中最后一次出现的数v的下标

 

为了更清楚地说明问题,下面讨论一下当循环体内l和h之间(包含l和h)最后只剩3个和2个元素时的情况,初始元素个数超过3个的最后都会转化为这两种情况之一。

只剩3个元素时,可能出现2中情况,一种是其中里面含有v,另一种是不含v。假设v = 2,那么含有v的可能出现以下几种情况:

 

//二分查找算法版本B:在有序向量的空间[l,h)内查找元素v,0<=l<=h<=_size
int bisearch(int* A,int l,int h,int v)
{
    if(!A || l>h)
        return -1;
    int m;
    while(1<h-l)//每步迭代仅需做一次比较判断,有两个分支,成功查找不能提前终止
    {
        m=l+(h-l)>>1;//以中点为轴点
        (e<A[m])?hi=mi:lo=mi;//经比较后确定深入[l,m)或[m,h)
    }//出口时h=l+1,查找区间仅含一个元素A[l]
    return (e==A[l])?l:-1;//查找成功时返回对应的秩,否则统一返回-1
}//有多个命中元素时,不能保证返回秩最大者;查找失败时,简单返回-1,而不能指示失败的位置

 

 

 1 int bisearch(int* A,int l,int h,int v)
 2 {
 3     if(!A || l>h)
 4         return -1;
 5     int m;
 6     while(l<h)
 7     {
 8         m=l+(h-l)>>1;
 9         (e<A[m])?hi=mi:lo=mi+1;
10     }
11     return l--;
12 }

 

posted @ 2015-03-31 10:19  daijkstra  阅读(194)  评论(0编辑  收藏  举报