【问题标题】:What is the time complexity for a clear function is std::map according to big O?根据大 O,明确函数的时间复杂度是 std::map 是多少?
【发布时间】:2012-06-12 09:27:12
【问题描述】:

清除函数的时间复杂度为 std::map 是多少?
我说它是 O(1) 对吗?

【问题讨论】:

  • 它必须销毁所有的成员对象。听起来像 O(n)。
  • @BoPersson:但是如果元素有一个微不足道的析构函数呢?
  • 然后它会运行得更快。 :-) 实际花费的时间是k*n,其中k 取决于成员对象的类型。但是在大 O 条件下,即使 k 非常小,它仍然是 O(n)。
  • @BoPersson:也许,也许不是。 is_trivially_destructible 是一个编译时特征,因此它可以在编译时推断出不需要这样的操作。
  • @jrok: 恐怕map 必须在每个节点上调用deallocate获取更多代码。

标签: c++ map time-complexity


【解决方案1】:

标准在 [associative.reqmts]/8 表 102 中说:

a.clear() a.erase(a.begin(), a.end()) 线性于a.size()

所以它实际上被要求为 O(N)。


编辑:总结各个方面。

要删除一个节点,map 会执行两个操作:

  1. 调用分配器destroy方法销毁元素
  2. 调用分配器deallocate方法释放节点占用的内存

前者可以在代码中省略(检查is_trivially_destructible),实际上它通常在例如vector中完成。不幸的是后者更棘手,并且不存在任何特征,因此我们必须依赖优化器。

不幸的是,即使通过内联优化器可以完全删除destroy 和deallocate 节点,恐怕它也无法意识到树遍历现在是无用的并优化 离开了。因此,您最终会在树的 Θ(N) 遍历中结束,并且每一步都没有做任何事情......

【讨论】:

  • +1 终于有人引用这个“唯一可靠”的来源了。
  • 自从发布问题以来,很明显,任何非平凡析构函数的最坏情况行为都是 O(N),但标准所说的线性在那里并不能确认最坏情况而不是消除实现可以达到 O(1) 的可能性?
  • @TonyDelroy:这是一个有趣的问题。标准规定它不能比 O(N) 差,但我当然希望它不会限制应用程序不进行优化。我认为 as-if 规则仍然成立,只要观察行为是类似的优化就可以应用。
  • O(1) 是 O(N),因此 O(1) 实现满足 O(N) 要求。
  • O(N) 只是说存在一个n 和一个k,因此对于N > n,被测量的事物(时间/CPU 滴答声)小于kN。无论n 满足等效的O(1) 要求和k = k/n 满足O(1) k 的要求,O(1) 过程都可以满足这一点。
【解决方案2】:

cplusplus reference site 声称它在容器大小方面具有线性复杂性,因为必须调用每个元素的析构函数。

【讨论】:

    【解决方案3】:

    因为它是一个模板,所以在编译时可能知道该类型的无操作中的销毁(例如std::map<int>),因此销毁成员的需要并不是推断必要的最坏情况的良好基础 -案例表现。尽管如此,编译器必须访问二叉树的每个节点,释放堆内存,并且节点的数量与元素的数量呈线性关系(erase() 只会使迭代器/引用/指向已擦除元素的指针无效,insert()不会使任何等无效。所有证据都证明了 1:1 的关系)。

    所以,它是线性的,但因为需要清理堆使用,即使不需要元素析构函数....

    (有趣的是,这意味着一个类似std::map<> 的关联容器——或者可能是std::map<> 本身带有一个聪明的自定义分配器——对于具有琐碎的无操作析构函数的元素来说,如果所有内存都在从可以在 O(1) 中“丢弃”的专用内存池中分配。)

    【讨论】:

    • 如果地图分配在一个竞技场或对象池上,会在 O(1) 内释放内存怎么办?
    • 我已经挑战了析构函数调用(对于trivially_destructible 值),现在让我挑战内存释放。标准是否规定map 不能将分配的节点保留在池中以供以后重用;)?
    • @DeadMG:恐怕通用模板还是会在每个节点上调用deallocate,它无法知道分配器是基于池的。
    • @MatthieuM:您可以提供一个自定义分配器,但可以进行简单的解除分配...这又在编译时解决。
    • @DeadMG:实际上,这很有趣。因为它表明,分配器可以向调用者公开更多的特征,而不是 destroy 和 deallocate 是无操作的,以便调用者可以利用它;不幸的是,这将是非标准的。
    【解决方案4】:

    据我所知,所有清理操作的复杂度都是 O(n),因为您需要一个一个地销毁这些对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-31
      • 2014-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 2020-10-13
      • 1970-01-01
      相关资源
      最近更新 更多