【问题标题】:distance between std::set begin() and std::set iterator in O(logn)在 O(logn) 中 std::set begin() 和 std::set 迭代器之间的距离
【发布时间】:2012-09-13 20:25:48
【问题描述】:

我需要在 std::set 中找到一个元素的索引。该索引可以可视化为迭代器与开始的距离。 一种方法可以是:

for(int i = 0, set<int>::iterator it = s.begin(); it != iteratorToBeFound; ++it, ++i);

这显然需要 O(n) 时间。但是我们知道,通过set内部实现的二叉搜索树到根的距离可以在O(log n)时间内找到。

他们有什么方法可以在 C++ 集中找到 O(log n) 时间的索引吗?

【问题讨论】:

  • 为什么需要索引?
  • 你确定可以在二叉搜索树中找到O(log n)时间的距离吗? set 通常是一棵红黑树,它在每个节点上没有太多关于其左右子树中分别有多少元素的信息。请记住,您不是在寻找直接到根的距离,而是在寻找您拥有的叶子左侧的叶子总数。
  • @SteveJessop:哦,那么他们没有办法计算 R-B 树中 O(logn) 中的索引吗?

标签: c++ stl iterator set std


【解决方案1】:

您可以使用函数std::set&lt;&gt;::find 搜索元素x 并计算distance 到集合的第一个迭代器。

std::distance(s.begin(), s.find(x))

然而,因为 cmets 表示距离的运行时间取决于所使用的迭代器的类型。在集合的情况下,这是一个双向迭代器,距离为 O(n)。

【讨论】:

  • 不过,那是O(log n + m)。但你能做的最好,AFAIK。
  • 但是std::distance在这里是O(N)。
  • 我知道 std::distance 但这与问题中的实现方式相同,并且绝对是 O(n)。
【解决方案2】:

您可以使用已排序的std::vector&lt;int&gt;。如果已排序,则可以在O(log n) 中找到元素。你可以在恒定时间内找到距离O(1)

通过排序向量,我的意思是在每次插入之后(或在多次插入之后),你会做std::sort(v.begin(), v.end());

如果您在 std::set&lt;T&gt; 中的类型不像 int 那样轻 - 您可以同时保留 - std::set&lt;T&gt; 和迭代器的排序向量 std::vector&lt;std::set&lt;T&gt;::iterator&gt;。但让这些结构保持同步并非易事。也许您可以在T 中添加一些类似的位置?或者保留std::set&lt;std::pair&lt;T,int&gt;, comp_first_of_pair&lt;T&gt;&gt;,其中comp_first_of_pair 只是让set 仅按T 排序,而第二个int 是为了保持位置在集合中?

只是一些想法 - 甚至有O(1) 距离时间...

【讨论】:

  • 但是在每次插入 std::vector 后进行排序会花费我 O(nlogn)。优势在哪里?
  • 1) 您只能在一系列连续插入之后进行排序。 2) std::set&lt;&gt; 的插入成本是 O(log n) - n 次插入:O(n Log n)。 3) 也许你 insert 一次 - 但多次测试距离....
【解决方案3】:

您可以在 O(log(N)) 中找到具有有序集合的集合中元素的索引:https://www.geeksforgeeks.org/ordered-set-gnu-c-pbds/。这被实现为红黑树。我知道这个话题很老了,但它可能对未来的读者有所帮助。

【讨论】:

  • 与其链接到外部资源,不如摘录其中的相关部分并将其包含在您的答案中。
  • 非常好的代码,我可以解决我遇到的一个问题,瓶颈是 std::setdistance 是 O(N)。这就像魔术一样!
【解决方案4】:

您不能将 matematics 与双向迭代器一起使用。所以唯一可以接受的方法是自己计算(你插入集合中的 int 比 X 少多少)。

但是,如果您将“数据收集”和“数据使用”阶段完全分开 - 可能值得将 std::set 替换为排序的 std::vector >。它更难维护,但有自己的好处,包括迭代器数学(因此您可以使用 std::binary_search 使用 O(log n) 进行搜索,使用 O(1) 获得距离)

【讨论】:

    【解决方案5】:

    如果计算索引真的是您的瓶颈,那么我看到了 2 个选项:

    • 存储索引。在节点本身或单独的std::map 中。 当然,这意味着您必须保持此缓存更新。
    • 使用std::vector。这并不像最初看起来那么糟糕。 如果您始终对向量进行排序,则可以像 set 一样使用它。 性能将类似于set。 最大的缺点是:节点可能会被复制很多。 (这可以通过使用指针来补偿,boost:shared_ptrstd::unique_ptr [c++11 only])
      要查找元素,请使用 std::lower_bound
      而不是 insert/push_back 你这样做:insert( lower_bound(b,e,x), x )

    【讨论】:

    • insert 是向量中的 O(n) 函数
    • 并且记住对实现它的容器使用专门版本的 lower_bound。较新的将 std::lower_bound 用于有序(多)映射和(多)集
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-03
    • 2012-04-25
    • 1970-01-01
    • 2011-11-21
    • 2015-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多