最近,在处理C ++中的编程问题时,我遇到了一些有趣的东西。我的算法使用了一个非常大的集合,并且会在其上使用std :: lower_bound很多次。然而,在提交我的解决方案之后,与我在纸上做的数学证明我的代码足够快,它最终变得太慢了。代码看起来像这样:
using namespace std;
set<int> s;
int x;
//code code code
set<int>::iterator it = lower_bound(s.begin(),s.end(),x);
然而,在从一个伙伴那里得到一个使用set :: lower_bound的提示之后,所讨论的算法比以前更快地工作了waaaaaaaay,它跟着我的数学。更改后的二进制搜索:
set<int>::iterator it = s.lower_bound(x);
我的问题是这两者之间的区别是什么?为什么一个人工作得多,比另一个人快得多?是不是low_bound应该是具有复杂度O(log2(n))的二元搜索函数?在我的代码中,它最终比那更慢。
答案 0 :(得分:3)
std::set
通常实现为自平衡树,其中包含一些类似列表的结构。知道了这个结构,std::set::lower_bound
将遍历树,知道树结构的属性。这里的每一步都意味着遵循左或右子分支。
std::lower_bound
需要运行类似于对数据进行二进制搜索的操作。但是,由于std::set::iterator
是双向的,因此速度要慢得多,需要在已检查的元素之间进行大量增量。因此,元素之间的工作更加激烈。在这种情况下,算法将检查A和B之间的元素,然后调整A或B中的一个,找到它们之间的元素,然后重复。
答案 1 :(得分:1)
std::lower_bound
是泛型二进制搜索算法,适用于大多数STL容器。 set::lower_bound
旨在与std::set
一起使用,因此它利用了std::set
的独特属性。
由于std::set
通常被实现为红黑树,可以想象std::lower_bound
遍历所有节点,而set::lower_bound
只是遍历树。
答案 2 :(得分:1)
std::lower_bound
始终保证O(log n)
比较,如果传递O(log n)
,则只保证RandomAccessIterator
时间,而不仅仅是ForwardIterator
它不提供常数时间std::advance
。
同一算法的std::set::lower_bound
实现能够使用结构的内部细节来避免这个问题。
答案 3 :(得分:1)
阅读std:lower_bound的API后: std:lower_bound
在非随机访问迭代器上,迭代器进展产生了平均N的额外线性复杂度。
我认为STL集正在使用非随机访问迭代器,因此如果在STL集上使用它不会进行O(lg N)二进制搜索