【问题标题】:associative / random access container关联/随机访问容器
【发布时间】:2012-06-03 04:12:44
【问题描述】:

我正在寻找一种数据结构来保存唯一元素的无序集合,这将支持以下操作

  1. 在集合中任意位置插入/删除元素
  2. 查询元素是否存在
  3. 访问随机元素

天真地,1 和 2 建议使用关联容器,例如unordered_set,但是 3 在元素数量上是线性的。使用随机访问容器,例如vector,使 3 变得容易,1 可以在 O(1) 中完成,但是 2 又是 O(N)。

问题是是否有一种已知的方法可以解决这种线性复杂性?

编辑:通过 3 中的随机元素,我的意思是:给定 N 个元素的任意顺序,检索元素编号 j,其中 j 介于 0 和 N-1 之间。对于std::vector,它只是下标,对于std::liststd::set,它从begin() 等开始将列表/集迭代器递增j 次。

【问题讨论】:

  • 通过哪个键访问随机元素?如果它是无序的,那么它就不能是索引——这意味着 std::set 或 std::hash_map 或类似的。
  • @lezebulon:对于 std::set 3 仍然是 O(N),不是吗?
  • 不,应该是 O(logn)。 std::set 使用红黑树实现。
  • @RogerLipscombe:通过索引,是的。给定 N 个元素的任意顺序,我想得到一个元素编号 j,其中 j 是从 0 到 N-1 的整数。
  • 如果你想要一个“无序集合”,插入和删除“集合中的任何位置”没有多大意义。

标签: c++ data-structures


【解决方案1】:

最适合您的任务的两个标准容器是 - 正如您所说,vector 与 1. 和 2. 在 O(n) 和 3. 在 O(1) 和 set 与 1。和 2. 在 O(log n) 中和 3. 在 O(n) 中。根据数据结构的大小,算法复杂性并不那么重要。 vector 具有数据局部性的额外优势,因此可以更好地利用 CPU 缓存。

如果元素的实际顺序无关紧要,vector 中的插入可以在摊销 O(1) (push_back) 中完成,如果您在 @987654327 中删除可以在摊销 O(1) 中完成@要删除的元素与最后一个元素并删除。

如果你真的有一个大数据结构,你可以使用Boost.Multi-Index 来构建一个数据结构,其中 1. 是 O(n),2. 是 O(log n),3. 是 O(1)。但是,就像我说的,如果你的数据结构不是很大,vector 应该可以工作。

如果随机访问索引中的顺序无关紧要,可以在摊销 O(log n) (push_back) 中完成插入。对于删除,您不能使用 swap 技巧,因为这会使其他索引无效。

【讨论】:

  • 向量内任意位置的插入和删除如何摊销 O(1)?肯定是 O(n)。
  • @juanchopanza 我错过了任何地方-感谢您指出
  • 哦不,它是 O(1),因为我不必维护顺序:可以从任何地方与最后一个交换元素。当然,这对数据局部性不利。
【解决方案2】:

我一直在寻找这样的数据结构。

最近,我发现了一个很有前途的库,它具有您正在寻找的所有功能。

在 O(log n) 中查看具有随机访问的 cntree::set。

这里是链接。 http://dl.dropbox.com/u/8437476/works/countertree/index.html

虽然它似乎正在开发中,但我认为它非常有用。

【讨论】:

  • 这看起来很有趣。这个库的状态如何?
  • 其实,我不是说出图书馆确切状况的合适人选。至少我发现每个容器的基本功能都按我的预期工作。我在几个项目中使用了这个库,没关系。但我不确定它是否足够稳定。
  • 源码中有库作者的邮箱。我觉得直接问他比较好。
【解决方案3】:

取决于您对#3 std::unordered_set 的确切需求可能非常合适。

我正在寻找具有上述属性的容器,以便我可以遍历所有类似于for(int i = 0; i < myset.size(); ++i) process(myset[i]); 的元素。 我发现this page 描述了std::unordered_set::bucket_count()std::unordered_set::begin(size_t bucket_number)std::unordered_set::end(size_t bucket_number)

如果您有 OpenMP 循环,这将变得非常方便,因此您可以编写:

std::unordered_set<Element> myset;

#pragma omp parallel for
for(int i = 0; i < myset.bucket_count(); ++i) {
   for(auto it = myset.begin(i); it != myset.end(i); ++it)
      processElement(*it);
}

这仍然不允许您直接访问 myset[i],但它非常接近,因为您可以访问编号存储桶中的元素。

【讨论】:

    【解决方案4】:

    std::unordered_set。如果使用索引j 作为键,访问元素不是 O(N),而是 O(1)。

    如果您有一个要用于查找的唯一索引并且您不关心其他排序,那么您还打算使用什么作为关联容器的键?

    【讨论】:

    • std::unordered_set 支持索引吗?
    • 支持key访问,为什么不用你的“index”作为key呢?
    • 如果您需要使用其他东西(您没有提到)作为键并且想要使用其他东西作为“索引”,您可以保留一个单独的 std::vector&lt;std::unordered_set&lt;T&gt;::iterator&gt; 以便您可以索引进入向量以获取元素的迭代器,或使用boost::multi_index(可以以相同的方式使用,但自动管理索引,而不需要单独保存。)
    • 只是钥匙,没有别的。我所说的 3. 的意思是:给定 N 个任意顺序的键,我希望能够检索一个元素编号 j,其中 j 从 0 到 N-1。并且键本身不一定是从 0 到 N-1 的整数。
    • @Zhenya 但是你说你想要一个无序的集合,那么这个j 数字如何映射到集合中的元素?
    猜你喜欢
    • 1970-01-01
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    • 2019-07-18
    • 2013-11-29
    • 1970-01-01
    相关资源
    最近更新 更多