简体   繁体   中英

faster erase-remove idiom when I don't care about order and don't have duplicates?

I have a vector of objects and want to delete by value. However the value only occurs once if at all, and I don't care about sorting.

Obviously, if such delete-by-values were extremely common, and/or the data set quite big, a vector wouldn't be the best data structure. But let's say I've determined that not to be the case.

To be clear, if my code were C, I'd be happy with the following:

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;
        }
    }
}

It seems that the "modern idiom" approach using std::algos and container methods would be:

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

But that should be far slower since for a random existent element, it's n/2 moves and n compares. My version is 1 move and n/2 compares.

Surely there's a better way to do this in "the modern idiom" than erase-remove-idiom? And if not why not?

Use std::find to replace the loop. Take the replacement value from the predecessor of the end iterator, and also use that iterator to erase that element. As this iterator is to the last element, erase is cheap. Bonus: bool return for success checking and template ing over 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;
}

Surely there's a better way to do this in "the modern idiom" than erase-remove-idiom?

There aren't a ready-made function for every niche use case in the standard library. Unstable remove is one of the functions that is not provided. It has been proposed (p0041r0) a while back though. Likewise, there are also no special versions of algorithms for the special case of vectors that do not contain duplicates.

So, you'll need to implement the algorithm yourself if you wish to use an optimal algorithm. There is std::find for linear search. After that, you only need to assign from last element and finally pop it off.

Most implementations of std::vector::resize will not reallocate if you make the size of the vector smaller. So, the following will probably have similar performance to the C example.

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);
    }
}

C++ way would be mostly identical with 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();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM