zoukankan      html  css  js  c++  java
  • 二分查找那些事

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

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

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

    语义定义:在有序向量的区间[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 }
  • 相关阅读:
    Java制作证书的工具keytool用法总结
    JSP取得绝对路径
    JavaScript屏蔽Backspace键
    MyEclipse使用总结——MyEclipse文件查找技巧
    Java基础学习总结——Java对象的序列化和反序列化
    JavaScript自动生成博文目录导航
    Javascript 面向对象编程(一):封装(转载)
    JavaScript学习总结(十四)——JavaScript编写类的扩展方法
    JavaScript学习总结(十六)——Javascript闭包(Closure)
    JavaScript学习总结(十三)——极简主义法编写JavaScript类
  • 原文地址:https://www.cnblogs.com/daijkstra/p/4380124.html
Copyright © 2011-2022 走看看