在C ++程序中,我有一组普通旧数据,我需要能够有效地使用它:
我不必使用集合或地图类型,因为没有外部要求:
如果我必须在C中从头开始编写它,我将使用指数增长的动态数组,删除操作会将最后一个元素移动到释放的插槽中。那样删除就是O(1)。
如果我想使用C ++标准容器,似乎我可以(a)使用std::vector
但是手动编写删除操作或(b)使用std::list
。我有一个温和但明确的不喜欢链表。
这两种解决方案对我来说都是可以接受的,但我真的更喜欢两种方式:使用矢量但只使用标准操作。有没有办法做到这一点。
更新
下面的评论表明我不是在寻找一般的删除操作。我的问题要求我经常迭代集合,并且在每次传递时我都希望发现必须删除一些元素。
无论删除次数多少,我都可以在O(n)时间完成整个操作。但这显然是一个定制的操作,我应该知道我需要编写自己的循环。令人惊讶的是remove_if
几乎解决了我的问题。如果我在迭代过程中唯一需要做的就是识别过时的元素,那么remove_if
就可以完成这项工作,但我也需要做其他处理。
答案 0 :(得分:2)
实际上,这已经在标准库中了:
std::vector
std::remove_if
后跟std::vector::erase
删除数据,然后Erase-remove idiom可以在向量的最后操作(请参阅boost::remove_erase_if)。或者,您可以查看std::vector::push_back
,但我不知道这是否会不必要地保留订单。但至少最后它是O(n)。std::vector::insert(end)
(或{{3}}添加数据以有效插入整个范围) std::remove_if
还允许您在一次迭代中删除一大堆元素。
并且,可能最重要的是,由于您的数据是连续存储的,迭代将最快 - >没有缓存未命中。
答案 1 :(得分:1)
这是一个擦除功能,可以从容器的末端移动元素:
template <typename Vector>
void unordered_erase(Vector& v, typename Vector::iterator it) {
*it = std::move(v.back());
v.pop_back();
}
答案 2 :(得分:0)
您可以在向量中为delete
函数编写一个包装器,其中将最后一个元素复制到要删除的位置,然后在最后一个元素处调用标准erase
函数。擦除最后一个元素将是O(1)。
它会是这样的:
void delete(vector<int> data, int position)
{
data[position] = data[data.size() - 1];
data.erase(data.begin() + data.size() - 1);
return;
}
我假设要删除一个数字,您将给出删除它的位置。如果是其他内容,请注明。
答案 3 :(得分:0)
这取决于集合的大小,每个元素的大小等。根据bog标准向量的大小和性能,您可以创建一个新的向量,只执行一次旧的向量并仅按下项目不配。那是O(N)。
标准库有std :: copy_if。例如,以下内容从第一个向量中删除所有值2。剩下的就是第二个。
#include <vector>
#include <algorithm>
#include <iterator>
int main(void)
{
std::vector<int> v1 = { 1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9, 2, 3, 2, 2};
std::vector<int> v2;
std::copy_if(v1.begin(), v1.end(), std::back_inserter(v2), [](int value) {
return value != 2;
});
}
在我的性能测试中(有50,000,000个随机数),这比remove_if方法略慢:
第32版:copy_if(1412),remove_if(1120)
第64版:copy_if(1420),remove_if(1141)
答案 4 :(得分:0)
解决整个问题的可能方案:
std::vector<MyClass> v;
// ...fill v...
v.erase(std::partition(v.begin(), v.end(), [](MyClass& element) -> bool {
// ...process element and return false if element should be deleted.
}), v.end());
我知道它已经很晚了,但有人可能仍觉得它很有用。
答案 5 :(得分:0)
这是remove_if的替代方法,可以在不维持订单的情况下进行交换。
/// faster remove_if, but it doesn't keep order.
/// Swaps matched items to back; returns iterator to start of matched items
template <typename C, typename F>
inline typename C::iterator swapBackIf(C & v, F comp) {
auto iter = v.begin();
auto rear = v.end();
for (iter; iter < rear; ++iter) {
auto const& ele = *iter;
if (comp(ele)) {
std::iter_swap(iter, --rear);
}
}
return rear;
}