lower_bound & upper_bound

##1.lower_bound

lower_bound(T A[],int first,int last,const T &v)

输入的元素: A [ f i r s t , l a s t ) A[first,last) A[first,last),不包含最后last所指的元素。
目标:有序数组当中查找一个元素,如果这个元素存在则返回这个元素第一次出现的位置,否则返回这个元素该插入的位置,也就是在这个位置插入之后,数组还是有序的。

  • 如果存在,那么就是返回第一次出现的位置,也就是所有小于目标元素的最后一个元素的下一个位置。
  • 如果不存在那么返回插入的位置,插入的位置的元素值,肯定是大于目标元素,或者是末尾之后的位置。所以我们传递的参数需要包含最后一个位置的下一个位置
  • 每次丢弃一半元素之后,目标元素的位置,应该不会在小于目标元素的左边。假设每步循环中$mid = (first+last)/2 $
    1.那么如果 $v \gt A[mid] $ 那么目标元素的位置不可能在$A[first,mid] 即 使 是 存 在 即使是存在 使A[k] = v 那 么 那么 k \in [mid+1,last) 。 所 以 查 找 的 范 围 应 该 变 成 。所以查找的范围应该变成 [mid+1,last)$
    2.如果$v \le A[mid] $ ,如果等号成立,那么目标元素第一次出现的位置不可能在 [ m i d + 1 , l a s t ) [mid+1,last) [mid+1,last)范围内,如果等号不成立,也就是 v < A [ m i d ] v \lt A[mid] v<A[mid]那么v第一次出现或者插入的位置不可能在 [ m i d + 1 , l a s t ] [mid+1,last] [mid+1,last]范围内。因为插入的位置,最多到 m i d mid mid这个位置,也就是前面的所有元素都小于 v v v而$A[mid] > v $
    所以这个时候的查找范围应该是 [ f i r s t , m i d ) [first,mid) [first,mid)
  • 循环的条件保持着在一个前闭后开的区间内查找。
  • 通过上面的分析可以看出来,每次如果丢弃左边元素,那么 f i r s t first first根据 m i d mid mid向前一步,如果丢弃右边元素,那么 l a s t last last使用 m i d mid mid作为下次查找的最后一个元素的下一个位置。每次丢弃左边会前进一步,丢弃右边则不会向左前进。直到两个指针相遇。 l a s t last last指向最后一个元素的下一个位置, f i r s t first first指向第一个元素的位置。
int lower_bound(int A[],int first,int last,const int &v){
     while(first != last){
          int mid = first + (last - first )/2;
          if( A[mid] < v) // A[first,mid] < v A[mid+1,last] >=v
               first = mid + 1;
          else
               last = mid;
     }
     return first;
}

给出模板的写法:

template<class ForwardIterator,typename T>
     ForwardIterator lower_bound(ForwardIteratorfirst,
					     ForwardIterator last,const T& v){
          while(first != last){
               ForwardIterator mid = first + distance(first,last)/2;
               if(*mid < v)// [first,mid] is less than v
                    first = ++mid;
               else
                    last = mid;
          }
          return first;
 }

下面给出SGI STL里面的写法,以供参考:

_ForwardIter __lower_bound(_ForwardIter __first, _ForwardIter __last,
                           const _Tp& __val, _Distance*) 
{
  _Distance __len = 0;
  distance(__first, __last, __len);//len = last - first
  _Distance __half;
  _ForwardIter __middle;

  while (__len > 0) {
    __half = __len >> 1;
    __middle = __first;
    advance(__middle, __half);// mid - first +1 = half;
    if (*__middle < __val) {
      __first = __middle;
      ++__first;
     /* [first,mid] length is half.  half +1+leftlength=len */
      __len = __len - __half - 1; 
     /*len should be length of [mid+1,last)
	   last - (mid+1) is the number of element of [mid+1,last)
     */
    }
    else
      __len = __half;
  }
  return __first;
}

##2.upper_bound

简化后的函数原型:

int upper_bound(int A[],int first,int last,const int &v);

输入:一个有序数组,或者数组的一部分。一个待查找的元素。$A[first,last) , 待 查 找 的 元 素 ,待查找的元素 ,v$.
输出:如果目标元素在有序数组当中,找到元素最后一次出现的位置。否则找到这个元素该插入的位置:插入之后有序数组还是有序的。

  • 类似于从右边顺序查找找到元素该插入的位置,不管这个元素是否存在。
    我们需要使用二分查找来进行查找这个位置。
  • 假设$mid = (first + last) / 2 $
  • 1.如果 A [ m i d ] > v A[mid] > v A[mid]>v
    说明如果 v v v在这个数组中存在,那么也不会存在于 A [ m i d , l a s t ) A[mid,last) A[mid,last)之间,而只可能存在于 A [ f i r s t , m i d ) A[first,mid) A[first,mid)当中。
    如果 v v v不存在那么从右边起第一个比它小的元素的位置也只可能存在于 [ f i r s t , m i d ) [first,mid) [first,mid)当中。而 v v v的插入位置就是第一个比他小的位置的下一个位置。所以下次查找的区间就是 [ f i r s t , m i d ) [first,mid) [first,mid)但是不包含mid,但是最后查找的结果可能就是 m i d mid mid这个位置。
  • 2.如果 A [ m i d ] ≤ v A[mid] \le v A[mid]v,那么 v v v插入的位置不可能在 [ f i r s t , m i d ] [first,mid] [first,mid],所以查找的位置应该 [ m i d + 1 , l a s t ) [mid+1,last) [mid+1,last),下面是实现的代码。每次要分析一种情况把区间的一半给丢弃了,确定查找的左右区间范围,这里面我采用了stl里面的写法,就是输入的区间不包含 l a s t last last指针所指向的位置的元素。
int upper_bound(int A[],int first,int last,const int &v){
     while(first != last){
          int mid = first + (last - first)/2;
          if( A[mid] > v)//  A[mid + 1,last) > v 
               last = mid;
          else
               first = mid + 1;
     }
     return first;
}

下面是SGI STL的写法:

_ForwardIter __upper_bound(_ForwardIter __first, _ForwardIter __last,
                           const _Tp& __val, _Distance*)
{
  _Distance __len = 0;
  distance(__first, __last, __len);
  _Distance __half;
  _ForwardIter __middle;

  while (__len > 0) {
    __half = __len >> 1;
    __middle = __first;
    advance(__middle, __half);
    if (__val < *__middle)
      __len = __half;
    else {
      __first = __middle;
      ++__first;
      __len = __len - __half - 1;
    }
  }
  return __first;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值