当我不在乎订单且没有重复项时,删除擦除的习语会更快吗?

时间:2019-12-30 03:30:55

标签: c++ algorithm erase-remove-idiom

我有一个对象向量,想按值删除。但是,该值只会出现一次,而且我也不在乎排序。

很显然,如果这样的按值删除非常普遍,并且/或者数据集很大,那么向量就不是最佳的数据结构。但是,假设我确定情况并非如此。

要明确的是,如果我的代码是C,我将对以下内容感到满意:

void delete_by_value( int* const piArray, int& n, int iValue ) {
    for ( int i = 0; i < n; i++ ) {
        if ( piArray[ i ] == iValue ) {
            piArray[ i ] = piArray[ --n ];
            return;
        }
    }
}

使用std :: algos和容器方法的“现代成语”方法似乎是:

v.erase(std::remove(v.begin(), v.end(), iValue), v.end());

但是那应该慢得多,因为对于一个随机存在的元素,它是n / 2个移动并且n比较。我的版本是1步,n / 2比较。

在“现代习语”中,肯定有比擦除-删除习语更好的方法吗?如果不是,为什么呢?

4 个答案:

答案 0 :(得分:2)

使用std::find替换循环。从end迭代器的前任中获取替换值,并将该迭代器用于该元素的erase。由于此迭代器位于最后一个元素上,因此erase很便宜。奖励:bool返回以进行成功检查并template结束int

template<typename T>
bool delete_by_value(std::vector<T> &v, T const &del) {
    auto final = v.end();
    auto found = std::find(v.begin(), final, del);
    if(found == final) return false;
    *found = *--final;
    v.erase(final);
    return true;
}

答案 1 :(得分:2)

  

在“现代习语”中,肯定有比擦除-删除习语更好的方法吗?

在标准库中,没有每种利基用例的现成功能。不稳定删除是未提供的功能之一。虽然已经proposed (p0041r0)一段时间了。同样,对于不包含重复项的向量的特殊情况,也没有特殊的算法版本。

因此,如果您想使用最佳算法,则需要自己实现算法。有std::find个用于线性搜索。之后,您只需要从最后一个元素开始分配,最后将其弹出即可。

答案 2 :(得分:0)

如果减小向量的大小,std::vector::resize的大多数实现将不会重新分配。因此,以下代码可能具有与C示例相似的性能。

void find_and_delete(std::vector<int>& v, int value) {
    auto it = std::find(v.begin(), v.end(), value);
    if (it != v.end()) {
        *it = v.back();
        v.resize(v.size() - 1);
    }
}

答案 3 :(得分:0)

C ++的方式与std::vector大致相同:

template <typename T>
void delete_by_value(std::vector<T>& v, const T& value) {
    auto it = std::find(v.begin(), v.end(), value);

    if (it != v.end()) {
        *it = std::move(v.back());
        v.pop_back();
    }
}