根据N4431草案,算法库中的函数std::binary_search
返回一个bool
, [binary.search]:
template<class ForwardIterator, class T> bool binary_search(ForwardIterator first, ForwardIterator last, const T& value); template<class ForwardIterator, class T, class Compare> bool binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp);
要求:根据表达式
e < value
和!(value < e)
或comp(e, value)
和!comp(value, e)
对[first,last)
的元素e
进行分区。同样,对于[first,last)
的所有e
,e < value
意味着!(value < e)
或comp(e, value)
implies !comp(value, e)
。如果在
[first,last)
范围内存在满足相应条件的迭代器i
,则返回:true
:!(*i < value) && !(value < *i)
或comp(*i, value) == false && comp(value, *i) == false
.复杂度:最多log2(last - first) + O(1)个比较。
有人知道为什么会这样吗?
大多数其他泛型算法要么返回元素的迭代器,要么返回与表示元素序列结束的迭代器等效的迭代器(即,在序列中要考虑的最后一个元素之后的一个),这是我所期望的。
此函数在STL 1994版本中的名称为isMember
。我想你会同意这个名字的函数应该返回bool
http://www.stepanovpapers.com/Stepanov-The_Standard_Template_Library-1994.pdf
它在c++中被分成多个不同的函数,至于原因,几乎不可能告诉为什么有人以这种或那种方式创造了一些东西。binary_search
将告诉您是否存在这样的元素。如果你需要知道它们的位置,使用lower_bound
和upper_bound
,它们将分别给出开始/结束迭代器。还有equal_range
,它同时给你开始和结束。
由于其他人似乎认为这是显而易见的,为什么它是这样被创造出来的,我将争论我的观点,为什么很难/不可能回答,如果你不是Alexander Stepanov或与他一起工作的人。
遗憾的是SGI STL FAQ根本没有提到binary_search
。它解释了list<>::size
是线性时间或pop
返回void
的原因。他们似乎并不认为binary_search
特别到足以记录它。
让我们看看@user2899162:
提到的可能的性能改进您可以在这里找到SGI STL算法binary_search
的原始实现。看一下它,你可以将它简化(我们都知道标准库中的内部名称有多糟糕)为:
template <class ForwardIter, class V>
bool binary_search(ForwardIter first, ForwardIter last, const V& value) {
ForwardIter it = lower_bound(first, last, value);
return it != last && !(value < *it);
}
正如您所看到的,它是根据lower_bound
实现的,并且获得了相同的性能。如果他们真的想让它利用可能的性能改进,他们就不会按照较慢的方式实现它,所以这似乎不是他们那样做的原因。
现在我们来看看它是一个简单的方便函数
作为一个简单的方便函数似乎更有可能,但是通过STL你会发现许多其他的算法,这可能是可能的。看看上面的实现,你会发现它只比std::find(begin, end, value) != end;
稍微多做一点,但是我们必须一直写它,并且没有一个返回bool
的方便函数。为什么是这里,而不是其他的算法?这不是很明显,也不能简单地解释。
总之,我发现它远不是显而易见的,我真的不知道我是否能自信而诚实地回答它。
二进制搜索算法依赖于严格弱排序。这意味着应该根据operator <
或具有相同保证的自定义比较器对元素进行分区。这意味着对于给定的查询,不一定只能找到一个元素。因此,需要lower_bound
、upper_bound
和equal_range
函数来检索迭代器。
标准库包含返回迭代器的二进制搜索算法的变体。它们分别叫做std::lower_bound和std::upper_bound。我认为std::binary_search
返回bool的基本原理是,在相同元素的情况下返回哪个迭代器并不清楚,而在std::lower_bound
和std::upper_bound
的情况下则很清楚。
也可能有性能方面的考虑,因为理论上std::binary_search
可以在多个等效元素和某些类型的情况下实现更好的性能。然而,至少有一种流行的标准库实现(libstdc++
)使用std::lower_bound
实现std::binary_search
,而且它们具有相同的理论复杂性。
如果你想获得一个值的迭代器,你可以使用std::equal_range,它将返回两个迭代器,一个在与你正在寻找的值相等的值范围的下界,一个在上界。
因为唯一的要求是值是排序的并且不是唯一的,所以没有简单的"find"可以返回你正在查找的元素的迭代器。如果只有一个元素与要查找的值相等,则两个迭代器之间的差值只有1。
下面是c++ 20中返回迭代器的二进制搜索选项:
template<typename RandomIt, typename T, typename Pred>
inline
RandomIt xbinary_search( RandomIt begin, RandomIt end, T const &key, Pred pred )
requires std::random_access_iterator<RandomIt>
&&
requires( Pred pred, typename std::iterator_traits<RandomIt>::value_type &elem, T const &key )
{
{ pred( elem, key ) } -> std::convertible_to<std::strong_ordering>;
}
{
using namespace std;
size_t lower = 0, upper = end - begin, mid;
strong_ordering so;
while( lower != upper )
{
mid = (lower + upper) / 2;
so = pred( begin[mid], key );
if( so == 0 )
{
assert(mid == 0 || pred( begin[mid - 1], key ) < 0);
assert(begin + mid + 1 == end || pred( begin[mid + 1], key ) > 0);
return begin + mid;
}
if( so > 0 )
upper = mid;
else
lower = mid + 1;
}
return end;
}
此代码只有在begin和end之间只有一个值与键匹配时才能正常工作。但是,如果您进行调试而NDEBUG没有定义,则代码将停止在调试器中。