[英]Why does std::binary_search use ForwardIterator, not RandomIterator?
[英]Why does std::binary_search return 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);
要求:
[first,last)
的元素e
根據表達式e < value
和!(value < e)
或comp(e, value)
和!comp(value, 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
。復雜性:最多記錄2 個(最后一個 - 第一個)+ O(1) 次比較。
有誰知道為什么會這樣?
大多數其他通用算法要么返回元素的迭代器,要么返回一個迭代器,它等效於表示元素序列末尾的迭代器(即,在序列中要考慮的最后一個元素之后),這就是我想要的預期的。
這個函數的名稱在1994年版的STL中是isMember
。 我認為你同意具有該名稱的函數應該返回bool
http://www.stepanovpapers.com/Stepanov-The_Standard_Template_Library-1994.pdf
它在C ++中被分成多個不同的函數,因為推理它幾乎不可能分辨出為什么有人以這種或那種方式做某事。 binary_search
將告訴您這樣的元素是否存在。 如果您需要知道它們的位置,請使用lower_bound
和upper_bound
,它們將分別給出開始/結束迭代器。 還有equal_range
,它同時為您提供開始和結束。
因為其他人似乎認為這很明顯是為什么它是以這種方式創造出來的,所以如果你不是亞歷山大·斯捷潘諾夫或與他合作的人,我會說我的觀點為什么很難/不可能回答。
可悲的是, 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;
之外,還有更多的事情要做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
,而且,它們具有相同的理論復雜性。
這是一個返回迭代器的 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;
}
僅當開始和結束之間只有一個與鍵匹配的值時,此代碼才能正常工作。 但是,如果您進行調試並且未定義 NDEBUG,則代碼將在您的調試器中停止。
如果你想在一個值上得到一個迭代器,你可以使用std :: equal_range ,它將返回2個迭代器,一個在下界,一個在值范圍的上限,等於你正在尋找的那個對於。
因為唯一的要求是值被排序而不是唯一的,所以沒有簡單的“find”會在你正在尋找的一個元素上返回一個迭代器。 如果只有一個元素等於你要查找的值,那么兩個迭代器之間只有1的差異。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.