为什么std::binary_search返回bool



根据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_boundupper_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_boundupper_boundequal_range函数来检索迭代器。

标准库包含返回迭代器的二进制搜索算法的变体。它们分别叫做std::lower_bound和std::upper_bound。我认为std::binary_search返回bool的基本原理是,在相同元素的情况下返回哪个迭代器并不清楚,而在std::lower_boundstd::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没有定义,则代码将停止在调试器中。

相关内容

  • 没有找到相关文章

最新更新