到目前为止,我一直将数组存储在一个向量中,然后循环遍历该向量以找到匹配的元素,然后返回索引。
在 C++ 中有没有更快的方法来做到这一点?我用来存储数组的 STL 结构对我来说并不重要(它不必是向量)。我的数组也是唯一的(没有重复的元素)并且是有序的(例如,一个及时向前的日期列表)。
由于元素已排序,因此您可以使用二进制搜索来查找匹配的元素。C++ 标准库具有std::lower_bound
可用于此目的的算法。为了清晰和简单,我建议将其包装在您自己的二进制搜索算法中:
/// Performs a binary search for an element
///
/// The range `[first, last)` must be ordered via `comparer`. If `value` is
/// found in the range, an iterator to the first element comparing equal to
/// `value` will be returned; if `value` is not found in the range, `last` is
/// returned.
template <typename RandomAccessIterator, typename Value, typename Comparer>
auto binary_search(RandomAccessIterator const first,
RandomAccessIterator const last,
Value const& value,
Comparer comparer) -> RandomAccessIterator
{
RandomAccessIterator it(std::lower_bound(first, last, value, comparer));
if (it == last || comparer(*it, value) || comparer(value, *it))
return last;
return it;
}
(C++ 标准库有一个std::binary_search
, 但如果范围包含该元素,它返回一个bool
: ,否则。如果你想要一个元素的迭代器,它就没有用了。)true
false
一旦有了元素的迭代器,就可以使用std::distance
算法来计算范围内元素的索引。
这两种算法在任何随机访问序列(包括std::vector
普通数组和普通数组)中都同样适用。
如果要将值与索引相关联并快速找到索引,可以使用std::map
或std::unordered_map
。您还可以将这些与其他数据结构(例如 astd::list
或std::vector
)结合起来,具体取决于您要对数据执行的其他操作。
例如,在创建向量时,我们还创建了一个查找表:
vector<int> test(test_size);
unordered_map<int, size_t> lookup;
int value = 0;
for(size_t index = 0; index < test_size; ++index)
{
test[index] = value;
lookup[value] = index;
value += rand()%100+1;
}
现在要查找索引,您只需:
size_t index = lookup[find_value];
使用基于哈希表的数据结构(例如 unordered_map)是一种相当经典的空间/时间折衷方案,当您需要进行大量查找时,这种“反向”查找操作的二分查找性能优于二分查找。另一个优点是它在向量未排序时也有效。
为了好玩:-) 我在 VS2012RC 中做了一个快速基准测试,将 James 的二进制搜索代码与线性搜索和使用 unordered_map 进行查找进行了比较,所有这些都在向量上:
对于〜50000个元素,unordered_set(x3-4)明显优于表现出预期的O(log N)行为的二分搜索,有点令人惊讶的结果是unordered_map在10000个元素之后失去了它的O(1)行为,大概是由于散列冲突,也许是一个实施问题。
编辑:无序地图的 max_load_factor() 为 1,因此应该没有冲突。对于非常大的向量,二分查找和哈希表之间的性能差异似乎与缓存相关,并且取决于基准中的查找模式。
在 std::map 和 std::unordered_map 之间进行选择讨论了有序映射和无序映射之间的区别。