首先,选择查找算法时,区间是否排序是一个至关重要的因素。
可以按是否需要排序区间分为两组:
A. count,find
B. binary_search,lower_bound,upper_bound,equal_range
A组不需排序区间, B组需要排序区间。
当一个区间被排序,优先选择B组,因为他们提供对数时间的效率。而A则是线性时间(其实就是从头到尾进行遍历)。
另外A组B组所依赖的查找判断法则不同,A使用相等性法则(查找对象需要定义operator==), B使用等价性法则(查找对象需要定义operator<,必须在相等时返回false)。
A组的区别
count:计算对象区间中的数目。
find:返回第一个对象的位置。
查找成功的话,find会立即返回,count不会立即返回(直到查找完整个区间),此时find效率较高。
因此除非是要计算对象的数目,否则不考虑count。
B组的区别
在排完序的数组中查找元素x,如何描述元素x的位置:用lower_bound表示x的下界,用upper_bound表示x的上界.也就是说要用一个区间来表示元素x的位置[lower_bound,upper_bound).看清楚了,这是一个前闭后开区间
binary_search:判断是否存在某个对象,并不能确定要查找的元素的位置.
lower_bound: 返回>=对象的第一个位置;
upper_bound: 返回>对象的第一个位置;
equal_range: 返回由lower_bound和upper_bound返回值构成的pair,也就是所有等价元素区间。
int lower(int *a, int sz, int x){ int l = 0, r = sz; while (l^r){ int mid = (l + r) >> 1; if (a[mid] < x)l = mid + 1; else r=mid; } return l; } int upper(int *a, int sz, int x){ int l = 0, r = sz; while (l^r){ int mid = (l + r) >> 1; if (a[mid] <= x)l = mid + 1; else r = mid; } return l; }
如上代码所示,lower_bound和upper_bound的区别就在于左指针l是小于还是小于等于.
二分代码有一个关键的地方:必须让l=mid+1.而不能写成l=mid,因为这会导致一个死循环:当l+1=r时,mid=(l+r)>>1=l.这就导致l,r不再发生变化,成死循环了.
equal_range有两个需要注意的地方:
1. 如果返回的两个迭代器相同,说明查找区间为空,没有这样的值
2. 返回迭代器间的距离与迭代器中对象数目是相等的,对于排序区间,他完成了count和find的双重任务
3.因为equal_range的作用相当于lower_bound和upper_bound之和,所以如果用到这两者时,尽量用equal_range,这样的效率为lgn,而分开使用的复杂度为2*lgn.
int a[] = {1,2,3,3,3,4,7}; int n = 7; pair<int*,int*>ans=equal_range(a, a + n, 3); cout << ans.first - a << " " << ans.second - a << endl;
输出2 5