最近(来自一个 SO 评论)我了解到这一点std::remove
并且std:remove_if
很稳定。我是否错误地认为这是一个糟糕的设计选择,因为它阻止了某些优化?
想象一下删除 1M 的第一个和第五个元素std::vector
。因为稳定性,我们不能remove
用swap来实现。相反,我们必须移动所有剩余的元素。:(
如果我们不受稳定性的限制,我们可以(对于 RA 和 BD 迭代器)实际上有 2 个迭代器,一个从前面,第二个从后面,然后使用交换来结束要移除的项目。我相信聪明的人可能会做得更好。我的问题是一般性的,而不是我正在谈论的特定优化。
编辑:std::sort
请注意,C++ 宣传零开销原则,还有std::stable_sort
排序算法。
EDIT2: 优化将类似于以下内容:
对于remove_if
:
- bad_iter 从头开始查找谓词返回 true 的那些元素。
- good_iter 从末尾查找谓词返回 false 的那些元素。
当双方都找到了预期的东西时,他们交换了他们的元素。终止时间为good_iter <= bad_iter
。
如果有帮助,请将其视为快速排序算法中的一个迭代器,但我们不会将它们与特殊元素进行比较,而是使用上述谓词。
EDIT3:我到处玩并试图找到最坏的情况(最坏的情况remove_if
- 请注意谓词很少为真),我得到了这个:
#include <vector>
#include <string>
#include <iostream>
#include <map>
#include <algorithm>
#include <cassert>
#include <chrono>
#include <memory>
using namespace std;
int main()
{
vector<string> vsp;
int n;
cin >> n;
for (int i =0; i < n; ++i)
{ string s = "123456";
s.push_back('a' + (rand() %26));
vsp.push_back(s);
}
auto vsp2 = vsp;
auto remove_start = std::chrono::high_resolution_clock::now();
auto it=remove_if(begin(vsp),end(vsp), [](const string& s){ return s < "123456b";});
vsp.erase(it,vsp.end());
cout << vsp.size() << endl;
auto remove_end = std::chrono::high_resolution_clock::now();
cout << "erase-remove: " << chrono::duration_cast<std::chrono::milliseconds>(remove_end-remove_start).count() << " milliseconds\n";
auto partition_start = std::chrono::high_resolution_clock::now();
auto it2=partition(begin(vsp2),end(vsp2), [](const string& s){ return s >= "123456b";});
vsp2.erase(it2,vsp2.end());
cout << vsp2.size() << endl;
auto partition_end = std::chrono::high_resolution_clock::now();
cout << "partition-remove: " << chrono::duration_cast<std::chrono::milliseconds>(partition_end-partition_start).count() << " milliseconds\n";
}
C:\STL\MinGW>g++ test_int.cpp -O2 && a.exe
12345678
11870995
erase-remove: 1426 milliseconds
11870995
partition-remove: 658 milliseconds
对于其他用途,分区更快,相同或更慢。让我不解。:D