##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;
}