【问题标题】:C++ STL set implementationC++ STL 集实现
【发布时间】:2013-05-08 06:13:47
【问题描述】:

为什么将 C++ 集合实现为二叉树而不是哈希集,与二叉树提供的 O(log n) 相比,它可以提供 O(1) 的平均案例复杂度?

【问题讨论】:

  • 这个问题可能更好地表述为为什么除了从一开始的有序集之外 STL 没有 hashset/unordered_set?当然,那么这个问题可能会因为无法回答或争论不休而被关闭。我只知道 Stepanov 最初的 STL 提案没有 hash_set(我不确定 SGI 何时将它添加到他们的 STL 中,但我认为那是在 1998 年标准最终定稿之前)。
  • @MichaelBurr 您的评论比迄今为止给出的任何答案都更接近实际问题的答案。
  • 因为与二叉树提供的 O(log n) 相比,哈希表还可以提供 O(n) 的最坏情况查找复杂度。
  • 对我来说名字看起来很糟糕。 set 应该是 ordered_set,而 unordered_set 应该是 set。排序不是集合所固有的。 map 也是如此。

标签: c++ algorithm data-structures stl


【解决方案1】:

这里有两个因素:

  • 二叉树和基于散列的关联集都很有用
  • 最初的 STL 只实施了前者,因为没有时间通过​​标准化的“政治”工作; SGI、GNU、MS、boost等多年来都提供了非标准的基于hash的版本,C++11引入unordered_set

下面将讨论这些要点。

两者都有用

有很多优点/缺点,并且都可以(从 C++11 开始)供程序员选择。基于哈希的集合不可用,因为根本就没有及时就实现达成一致,以便将它们包含在早期标准中。

  • set 迭代是按排序顺序进行的,而散列容器 unsorted_set 是有效的随机顺序遍历
    • set 支持lower_boundupper_bound,在unsorted_set 上提供这些是不切实际的
  • set 使用比较函数(默认情况下包含类型的 operator<,而 unordered_set 需要散列函数(为某些类型提供了默认值,但根据您的实际键,它们可能相当平庸,质量散列可能是时间消费)和一个关键的平等功能
  • 对于较小的 N 值,两者都可能更快 - 并非每个人都在处理数十亿个元素,因此即使从性能角度来看,提供选择也是明智的
  • 小对象的内存使用量可能存在显着差异,但我不确定哪些总体准则适用于各种实现,因此如果您关心,建议在您的程序中进行测量
  • 添加元素时,std::unordered_set 中的现有迭代器可能会失效(具体时间请参见 23.2.5p14),而std::map 中的迭代器永远不会因插入而失效(指针和对unordered_set 元素的引用仍然存在虽然有效)。

为什么早期的 C++ 标准没有基于哈希的集合

来自对 Stepanov 的采访:

问题:我在 D.Musser 站点中找到了两个哈希表实现,它们都可以工作并且非常智能 - 比类库中常见的哈希表要智能得多。为什么哈希表没有包含在 STL 中?

答案:政治。它们必须加入。我们新的 STL 实现确实包含它们。一般来说,我们需要开发一种机制来向 STL 添加东西。毕竟,STL 是一个可扩展的框架,它需要被扩展。缺少许多数据结构,例如,单链表、矩阵和图。 SGI愿意在扩展STL方面带头。

(完整采访http://www.stlport.org/resources/StepanovUSA.html

【讨论】:

  • unordered_sets 是通过桶链实现的,而不是开放寻址 (23.2.5p9)。因此,元素不会移动,尽管它们的迭代顺序可能会改变,因此对元素的引用在元素被删除之前是有效的 (23.2.5p14)。稀疏不是什么大问题。无论如何,它不需要为未使用的节点维护空间,只需要为桶头(只是指针)维护空间。不过,你的前两点很好。
  • @rici: 啊……没有意识到这些值不是按值存储的……“随着元素被添加到无序的关联容器中,桶的数量会自动增加,因此每个桶的平均元素数保持在一个界限以下。重新散列使迭代器无效,更改元素之间的顺序,并更改哪些桶元素出现在其中,但不会使指针或对元素的引用无效。”。将相应地更正 - 谢谢。
【解决方案2】:

据 John Nagle 所说,从 2006 年发帖到comp.lang.c++.moderated

真正的原因是写这篇文章的人 规范的哈希表部分没有及时完成。 就是这样。

标准化过程就是这样。

【讨论】:

  • 这就是为什么不包括哈希集/映射的原因,但其意图始终是基于二叉树和哈希的关联容器都可用(我对问题的解读是它预设了一个选择两者之间)。
  • 这是唯一真正的答案。这两种容器在标准库中都是有用且可取的。一个根本没有及时发货。
  • 这太简单了。
【解决方案3】:

因为 C++ 集合是由T 的比较运算符排序的,这使得以可预测的方式迭代成员成为可能。如果您知道您将对集合所做的一切就是插入、测试成员资格和/或删除元素,那么自 C++11 以来实现哈希集的 std::unordered_set 就存在于此。

【讨论】:

  • 与我所拥有的几乎一字不差:p
  • 对不起。我不明白。 C++ 集合不是按 T 的比较运算符排序的,因为它们是作为二叉树实现的吗?或者是周围的其他方式?似乎是循环逻辑。
  • @user2380088 二叉树体现了排序;比较运算符的存在使排序成为可能。
  • 对,但为什么选择二叉树而不是哈希集?
  • @user2380088,因为虽然哈希集具有非常好的查找​​复杂性,但它不一定适合按顺序处理,因为哈希的顺序可能与键的顺序关系不大。
猜你喜欢
  • 2023-03-27
  • 2014-08-26
  • 2012-07-05
  • 2013-12-04
  • 2019-10-12
  • 2019-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多