我最近玩了std::unordered_set
。我怀疑我的STL版本会跟踪某些FILO数据结构中的非空桶(看起来像一个列表)。我想这样做是为了提供完整O(n)
的{{1}}时间遍历(其中std::unordered_set
表示n
中带有unordered_set
个桶的元素数量并且m
比m
大得多。这可以在n
时间内改善所有存储桶的天真遍历。
我已经测试过,确实遍历大而稀疏的O(m)
(unordered_set
- 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);
}
似乎[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 }
- begin
遍历以相反的顺序报告存储桶,它们变为非空(参见第一行和第三行)。插入已经非空的桶不会改变这种顺序(参见第二行和第四行)。
答案 0 :(得分:3)
简而言之:是的,标准保证了这一点。
所有迭代器都必须具有O(n)
遍历时间复杂度(其中n
是遍历的项目数)。这是因为迭代器上的每个操作都具有恒定的时间复杂度(O(1)
),包括将迭代器推进到一个位置。
从标准(第24.2.1节第8节):
所有迭代器类别只需要在常量时间内(摊销)可以为给定类别实现的那些函数。因此,迭代器的需求表没有复杂性列。
因此,在迭代std::unordered_set
的项目时,时间复杂度为O(n)
(n
项中的项目数量。)
上述引文的字面读数仅保证恒定时间操作可实现。这并不妨碍特定实现的时间复杂度比 realizable 更差。这可能是一个糟糕的选择,希望没有严肃的实现实际上这样做。
标准中唯一可以帮助解决这种歧义的地方是第24.4.4节第1节,其中标准可以说明std::advance
和std::distance
:
这些函数模板使用
+
和-
作为随机访问迭代器(因此,它们是恒定的时间);对于输入,转发和双向迭代器,它们使用++
来提供线性时间 的实施方式。
因此,前向迭代器上的++
操作(用于std::unordered_set
)暗示为一个恒定时间操作。
总之,虽然第一个引用的措辞含糊不清,但第二个引用证实了意图。