【问题标题】:Efficiently count number of entries between two std::multimap iterators有效计算两个 std::multimap 迭代器之间的条目数
【发布时间】:2026-02-03 03:10:02
【问题描述】:

我想在少于 O(N) 的时间内计算 std::multimap 的两个迭代器之间的条目数。有什么技巧或聪明的方法可以做到这一点吗?

由于std::multimap 有双向迭代器,我的理解是像std::distance 这样的东西可以在O(N) 时间内完成。

附加细节:multimap 的键是一个 N 元组。我试图在multimap 中查找其键的第一个元素为0 的条目数。它们键的第一个元素的选项是0 和1,multimap 使用严格的弱排序,其中第一个关键元素永远是最重要的。即,所有带 0 的元素都在带 1 的任何元素之前。

上下文:迭代器由equal_range 返回,它以对数时间运行。声明式地,我想测量范围的长度。

谢谢。

【问题讨论】:

  • 有一个计数器是不可能的吗?
  • 不是,但我不希望手动管理计数器,如果有另一种方法可以做到这一点,即 STL 内置或不需要单独的计数器。
  • map/set 不提供对可以做这种事情的树遍历位的访问,这总是让我感到厌烦。同样,std::lower_bound(map_iter, map_iter, V) 效率低下。

标签: c++ c++11 stl time-complexity multimap


【解决方案1】:

您想使用迭代器库中的std::distance 方法。参考地址为std::distance。这是主要参考:Iterator library

截至 2014 年 3 月 25 日的描述如下:

template< class InputIt >
typename std::iterator_traits<InputIt>::difference_type
    distance( InputIt first, InputIt last );

返回第一个和最后一个之间的元素数。

如果 last 不能通过(可能重复)从 first 递增 first 到达,则行为未定义。

参数
first - 指向第一个元素的迭代器
last - 指向最后一个元素的迭代器

类型要求
- InputIt 必须满足 InputIterator 的要求。操作更高效 如果 InputIt 还满足 RandomAccessIterator 的要求

返回值
第一个和最后一个之间的元素数。

复杂性
线性。
但是,如果 InputIt 额外满足 RandomAccessIterator 的要求,则复杂度恒定


请注意,上述细节可能会发生变化。直接导航至std::distance 参考页面可获得最准确的信息。

来自std::distance参考网页的示例代码:

include <iostream>
#include <iterator>
#include <vector>

int main() 
{
    std::vector<int> v{ 3, 1, 4 };

    auto distance = std::distance(v.begin(), v.end());

    std::cout << distance << '\n';
}

【讨论】:

  • 这里的距离是 O(n),所以这对 OP 没有帮助。
  • RandomAccessIterator 满足 BidirectionalIterator 的要求,但反之则不然。 en.cppreference.com/w/cpp/iterator
  • 那么它不会比 O(n) 更好。如果需要,我会删除答案。
【解决方案2】:

您正在寻找的是在 N3465 中提出的所谓的heterogeneous comparison lookup。它允许在用于存储Keyequal_range 成员函数中使用不同但兼容 比较函数。在您的情况下,查找比较运算符(第一个元组成员)将不同于但与元组字典比较一致。

不幸的是,根据this Q&A,该论文中只有一小部分被接受为 C++14 标准草案。但是,N3465 论文的作者也是实现了this feature 的Boost.MultiIndex 的作者。您可以按照 Boost.MultiIndex 文档emulate a std::multimap

一旦您使用了改编的boost::multiindex_container 的广义equal_range 成员函数,您就可以简单地对返回的迭代器执行std::distance()。复杂性在 equal_range 的容器大小中是对数的,在返回的迭代器范围的大小中是线性的。如果您对结果不感兴趣而只对计数感兴趣,那么还有一个通用的count() 成员函数会在相同的对数 + 线性时间内返回该结果。

【讨论】:

  • 有趣的是 boost 库改进了 std::multimap 容器和 boost::multiindex_container
  • @CPlusPlusOOAandD Boost 传统上是许多当前标准库功能已经实现和测试的地方(智能指针、哈希表等)。因此,来自例如的指导书籍作为有效的 C++ 来“熟悉 Boost”。顺便说一句,来自 N3465 的引述很好:“作者收到了一些报告,指出此功能仅作为使用 Boost.MultiIndex 代替标准关联容器的理由”
  • 我非常了解新标准库功能的 Boost 试验场。 MultiIndex 容器已经不是标准的一部分,这有点令人惊讶(例如,自 Boost 1.32.0 起可用)。我 2001 年的 Effective C++ 副本似乎很少有 Boost 材料。除了这个页面之外还有什么推荐:where can I find a good boost reference? - closed Boost 参考书?我目前正在使用wget 获取网站文档。
  • 快速回顾这个优秀的 Boost 参考:The Boost C++ Libraries 表示 Boost 站点文档的一个很好的替代品。 MultiIndex 材料的具体链接在这里:13.4 Boost.MultiIndex