【问题标题】:Binary_search in STL set over set's member function find?STL集合中的Binary_search超过集合的成员函数find?
【发布时间】:2014-01-04 01:48:48
【问题描述】:

为什么我们有上述两种方法来搜索集合中的元素?

还可以使用查找算法在列表或向量中查找元素,但这些提供成员函数以及成员函数预计比通用算法更快会有什么危害?

为什么我们需要删除算法并创建所有关于擦除删除的戏剧,其中删除只会移动元素,然后使用擦除删除实际元素..就像 STL 列表提供成员函数删除为什么不能其他容器只是提供删除功能并完成它?

【问题讨论】:

  • +1 好问题。我认为答案可能只是“他们搞砸了”......
  • @Mehrdad 我不认为“他们搞砸了”。我相信我可以回答这些问题,请检查我的答案。
  • @Ali:我刚刚阅读了您的答案,我认为它实际上并没有“回答”任何问题。你只是在总结现状,没有提供背后的原因。我从您的回答中得到的唯一信息是“我了解您的问题”。我真的确实认为他们搞砸了,因为 set.lower_bound 不应该在那里 -- std::lower_bound 应该专门从事同样的工作。
  • @Mehrdad "std::lower_bound 应该专门从事同样的工作。" 我也遇到过这种情况。显然它无法做到,请参阅Is there any technical reason why std::lower_bound is not specialized for red-black tree iterators? 至于“我刚刚阅读了您的答案,我认为它实际上并没有“回答”任何问题”,我很遗憾听到这个消息。在我看来,我给出了我们需要这些功能的充分理由。
  • @Ali:我不买那个页面上的答案。它说“虽然可能有父指针,但对树要求这样似乎不合适。”。我不认为那是真的。你怎么可能避免需要父指针?如果迭代器指向正确的位置,set::insert(iterator, value) 的时间复杂度是摊销的常数时间。但是没有父指针,我认为为了确保插入后树是平衡的,树必须每次都从根开始遍历,这是摊销的恒定时间...

标签: c++ c++11 stl


【解决方案1】:

STL 集合中的 Binary_search 超过集合的成员函数 find?
为什么我们有两种方式来搜索集合中的元素?

二分查找返回boolset::find() 以及迭代器。为了比较苹果和苹果,将set::find()std::lower_bound() 进行比较的算法也返回一个迭代器。

您可以将std::lower_bound() 应用于由一对(前向/双向/随机访问)迭代器指定的任意排序范围,而不仅仅是std::set所以有std::lower_bound() 是合理的。由于std::set 恰好是一个排序范围,你可以调用

std::lower_bound(mySet.begin(), mySet.end(), value);

但是

mySet.find(value);

调用不仅更简洁,而且效率更高。如果您查看std::lower_bound() 的实现,您会发现类似std::advance(__middle, __half); 的东西根据迭代器(无论是前向/双向/随机访问迭代器)具有不同的复杂性。在std::set 的情况下,迭代器是双向的,并且推进它们具有线性复杂性,哎呀!相比之下,std::set::find()保证以对数时间复杂度执行搜索。底层实现(在 libstdc++ 的情况下是红黑树)使其成为可能。 提供set::find() 也是合理的,因为它比在std::set 上调用std::lower_bound() 更有效。

还可以使用查找算法来查找列表中的元素或 向量,但是这些提供成员函数的危害是什么 以及成员函数预计比泛型更快 算法?

我不明白如何为列表或向量提供更快的成员函数,除非容器已排序(或具有某些特殊属性)。

为什么我们需要删除算法并创建所有关于擦除的戏剧 remove where remove 只会移动元素,然后使用擦除 删除实际元素..就像 STL 列表提供了一个成员 功能删除为什么其他容器不能只提供删除 功能并完成它?

我能想到两个原因。

是的,STL 严重缺乏许多便利功能。在整个容器上使用算法时,我经常觉得自己生活在一个开始和结束的地狱中;我经常证明我自己的接受容器的包装器,例如:

template <typename T>
bool contains(const std::vector<T>& v, const T& elem) {

    return std::find(v.begin(), v.end(), elem) != v.end();
}

这样我可以写

if (contains(myVector, 42)) { 

而不是

if (std::find(myVector.begin(), myVector.end(), 42) != myVector.end()) { 

不幸的是,您经常不得不自己滚动或使用 boost。为什么?因为标准化是痛苦和缓慢的,所以标准化委员会专注于更重要的事情。委员会成员经常贡献他们的空闲时间,而他们的工作却没有得到报酬。

现在从向量中删除元素可能会很棘手:您关心元素的顺序吗?您的元素是 POD 吗?您的异常安全要求是什么?

假设您不关心元素的顺序并且想要删除第 i 个元素:

std::swap(myVector[i], myVector.back());
myVector.pop_back();

甚至更简单:

myVector[i] = myVector.back(); // but if operator= throws during copying you might be in trouble
myVector.pop_back();

在具有移动语义的 C++11 中:

myVector[i] = std::move(myVector.back());
myVector.pop_back();

请注意,这些是O(1) 操作,而不是O(N)这些是标准委员会留给您的效率和异常安全注意事项的示例。 提供成员函数和“一刀切”不是 C++ 方式。

说了这么多,我再说一遍,希望我们有更多的便利功能;我理解你的问题。

【讨论】:

  • 提供 set::find() 也是合理的,因为它会产生更简洁的代码 是的,它更方便,但是对于两个集合 find 来说仍然存在 O(logn) 复杂性以及将使用下限的二进制搜索..所以我猜性能方面是相同的,但是 set find 更方便..Alos 只是好奇你能给我一个例子,说明 set 有一个成员函数 find 可以优化搜索相比到 binary_search 的下界的递归调用 ..因为 set 存储在一棵红黑树中..如果给定一个选项,您会使用 set.find 而不是 binary_search 吗?
  • @user3157360 至于您的第一条评论:欢迎!顺便说一句,在 Stackoverflow,我们不会说谢谢,而是点赞并接受答案。 :) 至于你的第二条评论:我已经修复并更新了答案。请阅读。
  • 我认为减少接口(例如,提高可维护性)也是标准库的目标——infamous exception of std::string。对于许多类型,唯一的成员函数是需要是成员函数并且不能写成非成员非友元函数的函数。请参阅isocpp.org/std/library-design-guidelines(“类签名希望接近最小”)
  • @DyP 谢谢。然而,这个问题引发了我即将发布的一个问题。敬请期待:)
  • 期待它 ;) -- 也许擦除删除习语很可怕是件好事,因为它的 O(n) 效率也很低,尤其是对于单元素擦除,应该替换为多次删除一次擦除。
【解决方案2】:

我会回答你的部分问题。 Erase-Remove 习语出自 Scott Meye 所著的《Effective STL》一书。至于为什么 remove() 实际上并没有从容器中删除元素,有一个很好的答案here,我只是复制部分答案:

关键是要意识到 remove() 的设计目的不仅仅是 容器,但在任意前向迭代器对上:这意味着它 实际上不能删除元素,因为任意迭代器对 不一定有删除元素的能力。

为什么 STL list 提供了一个成员函数remove,为什么其他容器不能只提供一个删除函数并完成它?我认为这是因为从 contiguous-memory 容器中删除特定值的方法比其他方法更有效。

【讨论】:

  • 真的,我同意 Scott meyers 关于移除的全部观点,但你不认为他们一开始就不应该这样设计它。让一个序列容器作为成员提供移除只是不必要的混淆功能(列表)和其他人不提供它:) 至少他们应该重命名删除以删除或删除列表我猜但是是的我们不能改变语言......而且即使在集合中我认为它只是搞砸了但希望在那里是一些具体的原因...bcoz Java set 不提供查找功能;
  • @user3157360 "Java set 不提供查找功能" SortedSet 接口确实提供了查找功能,只是有点扭曲,称为SortedSet.tailSet()。这是最接近std::set::find() 的值。请注意,Set 接口提供 Set.contains() 如果您只对包含感兴趣但不想访问元素本身。
  • 谢谢..所以只需检查集合是否包含该元素,但不要去那里更改元素..我明白了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多