C++ 应该使用插入排序或构造堆来提高性能?
我们有结构的大(100000+个元素)有序向量(运算符<重载以提供排序): 这并不完全是我们的代码看起来的样子,因为我知道这个示例可以通过不同的方式进行优化,但是它让您了解性能可能是一个问题,因为我在每次C++ 应该使用插入排序或构造堆来提高性能?,c++,algorithm,sorting,stl,boost,C++,Algorithm,Sorting,Stl,Boost,我们有结构的大(100000+个元素)有序向量(运算符您可能需要考虑使用 Read()/以避免对整个向量进行过度的重新分配。如果您知道它将增长到什么程度,您可以通过自己执行resrve()s来获得一些性能(而不是让Implementation使用内置的启发式自动执行) 执行二进制搜索以查找插入位置。然后调整大小并将插入点之后的所有内容向上移动1以腾出空间 思考:你真的想使用向量吗?也许集合或映射更好 与下界相比,二进制搜索的优势在于,如果插入点接近向量的末端,则不必支付θ(n)复杂度。使用二进制
推回
后进行排序
我认为我基本上有两种选择来提高性能:
std::sort
,以提高排序性能(在部分排序的向量上进行插入排序非常快)std::make_heap
和std::push_heap
来创建堆,以维护排序顺序- 我应该实现插入排序吗?Boost中有什么东西可以帮助我吗?
<强>我应该考虑使用堆吗?我该怎么做?
编辑: 谢谢你的回复。我知道我给出的示例远远不是最优的,它不能完全代表我现在代码中的内容。这仅仅是为了说明我所经历的性能瓶颈——也许这就是为什么这个问题没有获得很多赞成票的原因:) 多亏了你,最简单的答案往往是最好的,也许是我对问题的过度分析让我看不到最明显的解决方案。我非常喜欢您概述的直接插入到预排序向量的简洁方法
正如我所评论的,我现在只能使用向量,所以std::set、std::map等都不是一个选项。为什么不使用二进制搜索来查找插入新元素的位置呢?然后您将准确地插入到所需位置。有序插入不需要升压:
vectorMyTypes.insert(
std::upper_bound(vectorMyTypes.begin(), vectorMyTypes.end(), newType),
newType);
upper\u bound
提供了一个有效的插入点,前提是向量从开始就进行了排序,因此只要您只在元素的正确位置插入元素,就完成了插入。我最初说的是下限
,但是如果向量包含多个相等的元素,那么上限
选择需要较少工作的插入点
这确实需要复制O(n)个元素,但您说插入排序“快得惊人”,而且速度更快。如果不够快,您必须找到一种方法来批量添加项目并在最后进行验证,或者放弃连续存储并切换到维护顺序的容器,例如set
或multiset
堆不维护底层容器中的顺序,但适合优先级队列或类似队列,因为它可以快速删除最大元素。你说你想保持向量的顺序,但是如果你从来没有按顺序迭代过整个集合,那么你可能不需要对它进行完全排序,而这正是堆有用的时候。为什么不使用
注意:
boost::multi_index
不提供内存连续性,这是std::vectors
的一种属性,通过该属性,元素在单个内存块中彼此相邻地存储。您需要做几件事
resrve()
s来获得一些性能(而不是让Implementation使用内置的启发式自动执行)
调整大小
并将插入点之后的所有内容向上移动1以腾出空间集合
或映射
更好与下界相比,二进制搜索的优势在于,如果插入点接近向量的末端,则不必支付θ(n)复杂度。使用二进制搜索查找插入位置不会大大加快算法的速度,因为插入仍然是O(n)(考虑在向量的开头插入-必须将每个元素向下移动一个以创建空间) 树(又名heap)将被O(log(N))插入,性能更好 看
请注意,除非是平衡的树(例如AVL树),否则树在插入时仍将具有最差的O(N)性能。如果需要将大量元素插入到已排序的序列中,请使用
std::merge
,可能先对新元素排序:
void add( std::vector<Foo> & oldFoos, const std::vector<Foo> & newFoos ) {
std::vector<Foo> merged;
// precondition: oldFoos _and newFoos_ are sorted
merged.reserve( oldFoos.size() + newFoos.size() ); // only for std::vector
std::merge( oldFoos.begin(), oldFoos.end(),
newFoos.begin(), newFoos.end(),
std::back_inserter( merged );
// apply std::unique, if wanted, here
merged.erase( std::unique( merged.begin(), merged.end() ), merged.end() );
oldFoos.swap( merged ); // commit changes
}
void add(std::vector和oldFoos,const std::vector和newFoos){
std::向量合并;
//前提条件:oldFoos_和newFoos_已排序
merged.reserve(oldFoos.size()+newFoos.size());//仅适用于std::vector
合并(oldFoos.begin(),oldFoos.end(),
newFoos.begin(),newFoos.end(),
std::背面插入器(合并);
//如果需要,请在此处应用std::unique
merged.erase(std::unique(merged.begin()、merged.end()、merged.end());
oldFoos.swap(合并);//提交更改
}
根据Meyers的有效STL第23项,如果应用程序在3个阶段使用其数据结构,则应使用排序向量。从本书中可以看出,它们是:
vectorMyTypes.insert(
std::upper_bound(vectorMyTypes.begin(), vectorMyTypes.end(), newType),
newType);
void add( std::vector<Foo> & oldFoos, const std::vector<Foo> & newFoos ) {
std::vector<Foo> merged;
// precondition: oldFoos _and newFoos_ are sorted
merged.reserve( oldFoos.size() + newFoos.size() ); // only for std::vector
std::merge( oldFoos.begin(), oldFoos.end(),
newFoos.begin(), newFoos.end(),
std::back_inserter( merged );
// apply std::unique, if wanted, here
merged.erase( std::unique( merged.begin(), merged.end() ), merged.end() );
oldFoos.swap( merged ); // commit changes
}