std::unordered_set 迭代器遍历的复杂性

m8m*_*ble 7 c++ stl std c++11

我最近玩了一个std::unordered_set. 我怀疑我的 STL 版本会跟踪某些 FILO 数据结构(看起来像列表)中的非空存储桶。我想这样做是为了提供O(n)完整的时间遍历std::unordered_set(其中表示 a 中带有桶且远大于n的元素数量)。这改进了及时遍历所有桶的方式。unordered_setmmnO(m)

我已经测试过,确实遍历大型且非常稀疏的unordered_sets (带有begin- end)比简单遍历所有存储桶要快得多。

问题:这个遍历运行时间是由标准保证的吗?或者这只是我特定标准库的一个功能?


这是我可以使用的测试代码:

#include <iostream>
#include <vector>
#include <numeric>
#include <unordered_set>
using namespace std;

void test(vector<int> data, int alloc_size) {
   unordered_set<int> set(alloc_size);
   for (auto i: data) {
      set.insert(i);
   }

   for (size_t bidx = 0; bidx < set.bucket_count(); ++bidx) {
      cout << "[B" << bidx << ":";
      for (auto bit = set.begin(bidx); bit != set.end(bidx); ++bit) {
         cout << " " << *bit;
      }
      cout << "] ";
   }

   cout << "  {";
   for (auto const & d: set) {
      cout << d << " ";
   }
   cout << "}" << endl;
}

int main() {
   test({1, 2, 0}, 3);
   test({1, 2, 0, 7}, 3);
   test({18, 6, 11, 3, 13, 4}, 20);
   test({18, 6, 11, 3, 13, 4, 34}, 20);
}
Run Code Online (Sandbox Code Playgroud)

哪个打印:

[B0: 0] [B1: 1] [B2: 2] [B3:] [B4:]   {0 2 1 }
[B0: 0] [B1: 1] [B2: 7 2] [B3:] [B4:]   {0 7 2 1 }
[B0:] [B1:] [B2:] [B3: 3] [B4: 4] [B5:] [B6: 6] [B7:] [B8:] [B9:] [B10:] [B11: 11] [B12:] [B13: 13] [B14:] [B15:] [B16:] [B17:] [B18: 18] [B19:] [B20:] [B21:] [B22:]   {4 13 3 11 6 18 }
[B0:] [B1:] [B2:] [B3: 3] [B4: 4] [B5:] [B6: 6] [B7:] [B8:] [B9:] [B10:] [B11: 34 11] [B12:] [B13: 13] [B14:] [B15:] [B16:] [B17:] [B18: 18] [B19:] [B20:] [B21:] [B22:]   {4 13 3 34 11 6 18 }
Run Code Online (Sandbox Code Playgroud)

看起来begin-end遍历报告存储桶的顺序与它们变为非空的顺序相反(参见第一行和第三行)。插入已经非空的桶中不会改变这个顺序(参见第二行和第四行)。

San*_*ker 8

简而言之:是的,这是由标准保证的。

\n\n

解释

\n\n

所有迭代器都需要具有O(n)遍历时间复杂度(其中n是遍历的项目数量)。这是因为迭代器上的每个操作都具有恒定的时间复杂度 ( O(1)),包括将迭代器前进一个位置。

\n\n

根据标准(第 24.2.1 节 \xc2\xa78):

\n\n
\n

所有类别的迭代器仅需要那些可在恒定时间(摊销)内为给定类别实现的函数。因此,迭代器的需求表没有复杂性列。

\n
\n\n

因此,当迭代 a 的项目时std::unordered_set,时间复杂度为O(n)n集合中项目的数量)。

\n\n

不相信?

\n\n

从字面上看上面的引用只能保证恒定时间操作是可实现的。这并不能阻止特定实现的时间复杂度比可实现的复杂度更差。这可能是由于用词不当造成的,希望没有认真的实现真正做到这一点。

\n\n

标准中唯一可以帮助解决这种歧义的地方是第 24.4.4 \xc2\xa71 节,其中标准对std::advance和进行了如下说明std::distance

\n\n
\n

这些函数模板使用+-进行随机访问迭代器(因此,它们的时间是恒定的);对于输入、前向和双向迭代器,它们用于++提供线性时间\n 实现。

\n
\n\n

因此,++前向迭代器上的操作(如用于std::unordered_set)被暗示为恒定时间操作。

\n\n

总之,虽然第一句话的措辞含糊不清,但第二句话证实了意图。

\n