如果您允许我这么说,大多数答案听起来像是在各种方式地说“我不知道”,而没有真正承认他们不知道。虽然我基本上同意他们给出的建议,但他们似乎都没有直接回答你提出的问题:什么是盈亏平衡点。
公平地说,当我读到这个问题时,我也不太清楚。对于一个足够小的集合,线性搜索可能会更快;对于一个足够大的集合,二分搜索肯定会更快。然而,我从来没有真正去调查过盈亏平衡点的任何信息。然而,您的问题让我感到好奇,所以我决定写一些代码,以至少得到一些想法。
这段代码绝对是一个非常快速的hack(有很多重复,目前只支持一种类型的键等),但至少它可以给出一些期望值:
#include <set>
#include <vector>
#include <string>
#include <time.h>
#include <iostream>
#include <algorithm>
int main() {
static const int reps = 100000;
std::vector<int> data_vector;
std::set<int> data_set;
std::vector<int> search_keys;
for (int size=10; size<100; size += 10) {
data_vector.clear();
data_set.clear();
search_keys.clear();
int num_keys = size / 10;
for (int i=0; i<size; i++) {
int key = rand();
if (i % num_keys == 0)
search_keys.push_back(key);
data_set.insert(key);
data_vector.push_back(key);
}
for (int i=0; i<10; i++)
search_keys.push_back(rand());
long total_linear =0, total_binary = 0;
clock_t start_linear = clock();
for (int k=0; k<reps; k++) {
for (int j=0; j<search_keys.size(); j++) {
std::vector<int>::iterator pos = std::find(data_vector.begin(), data_vector.end(), search_keys[j]);
if (pos != data_vector.end())
total_linear += *pos;
}
}
clock_t stop_linear = clock();
clock_t start_binary = clock();
for (int k=0; k<reps; k++) {
for (int j=0; j<search_keys.size(); j++) {
std::set<int>::iterator pos = data_set.find(search_keys[j]);
if (pos != data_set.end())
total_binary += *pos;
}
}
clock_t stop_binary = clock();
std::cout << "\nignore: " << total_linear << " ignore also: " << total_binary << "\n";
std::cout << "For size = " << size << "\n";
std::cout << "\tLinear search time = " << stop_linear - start_linear << "\n";
std::cout << "\tBinary search time = " << stop_binary - start_binary << "\n";
}
return 0;
}
这是在我的机器上运行时得到的结果:
ignore: 669830816 ignore also: 669830816
For size = 10
Linear search time = 37
Binary search time = 45
ignore: 966398112 ignore also: 966398112
For size = 20
Linear search time = 60
Binary search time = 47
ignore: 389263520 ignore also: 389263520
For size = 30
Linear search time = 83
Binary search time = 63
ignore: -1561901888 ignore also: -1561901888
For size = 40
Linear search time = 106
Binary search time = 65
ignore: -1741869184 ignore also: -1741869184
For size = 50
Linear search time = 127
Binary search time = 69
ignore: 1130798112 ignore also: 1130798112
For size = 60
Linear search time = 155
Binary search time = 75
ignore: -1949669184 ignore also: -1949669184
For size = 70
Linear search time = 173
Binary search time = 83
ignore: -1991069184 ignore also: -1991069184
For size = 80
Linear search time = 195
Binary search time = 90
ignore: 1750998112 ignore also: 1750998112
For size = 90
Linear search time = 217
Binary search time = 79
显然这并不是唯一可能的测试(甚至远非最佳测试),但对我来说,即使有一点硬数据也比没有好。
编辑:值得注意的是,我认为使用两个向量(或者一个成对的向量)的代码与使用set或map的代码一样简洁。显然,你需要把它的代码放到自己的一个小类中,但我完全没有理由认为它不能像 map
一样向外界呈现 完全相同 的接口。事实上,我可能会直接称之为“tiny_map”(或者类似的名称)。
面向对象编程(在泛型编程中仍然存在一定程度上)的基本观点之一是将接口与实现分离。在这种情况下,你谈论的只是纯粹的实现细节,根本不需要影响接口。事实上,如果我正在编写一个标准库,我会考虑将其作为“小型映射优化”纳入其中——类似于常见的小字符串优化。基本上,只需在映射对象本身中分配10个(或更多)value_type
对象的数组,并在映射较小时使用它们,当映射增长到足以证明需要时,则将数据移动到树中。唯一真正的问题是人们是否经常使用 tiny maps,以此来证明这项工作是值得的。
binary_search
或lower_bound
即可获得O(log N)的搜索。 - Matthieu M.