Стирание элементов из вектора

Я хочу очистить элемент от вектора, используя метод стирания. Но проблема здесь в том, что элемент не гарантированно встречается только один раз в векторе. Он может присутствовать несколько раз, и мне нужно очистить все из них. Мой код выглядит примерно так:

void erase(std::vector<int>& myNumbers_in, int number_in)
{
    std::vector<int>::iterator iter = myNumbers_in.begin();
    std::vector<int>::iterator endIter = myNumbers_in.end();
    for(; iter != endIter; ++iter)
    {
        if(*iter == number_in)
        {
            myNumbers_in.erase(iter);
        }
    }
}

int main(int argc, char* argv[])
{
    std::vector<int> myNmbers;
    for(int i = 0; i < 2; ++i)
    {
        myNmbers.push_back(i);
        myNmbers.push_back(i);
    }

    erase(myNmbers, 1);

    return 0;
}

Этот код явно сработает, потому что я изменяю конец вектора, итерации через него. Каков наилучший способ достичь этого? То есть есть ли способ сделать это без повторения через вектор несколько раз или создания еще одной копии вектора?

Ответ 1

Используйте удалить/стереть идиому:

std::vector<int>& vec = myNumbers; // use shorter name
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end());

Что происходит, так это то, что remove сжимает элементы, которые отличаются от удаляемого значения (number_in) в начале vector, и возвращает итератор к первому элементу после этого диапазона. Затем erase удаляет эти элементы (значение которых не указано).

Ответ 2

Вызов стирания приведет к аннулированию итераторов, вы можете использовать:

void erase(std::vector<int>& myNumbers_in, int number_in)
{
    std::vector<int>::iterator iter = myNumbers_in.begin();
    while (iter != myNumbers_in.end())
    {
        if (*iter == number_in)
        {
            iter = myNumbers_in.erase(iter);
        }
        else
        {
           ++iter;
        }
    }

}

Или вы можете использовать std:: remove_if вместе с функтором и std::vector:: стереть:

struct Eraser
{
    Eraser(int number_in) : number_in(number_in) {}
    int number_in;
    bool operator()(int i) const
    {
        return i == number_in;
    }
};

std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end());

Вместо написания собственного функтора в этом случае вы можете использовать std:: remove:

std::vector<int> myNumbers;
myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end());

Ответ 3

  • Вы можете выполнять итерацию с использованием доступа к индексу,

  • Чтобы избежать сложностей O (n ^ 2) вы можете использовать два индекса, я - текущий индекс тестирования, j - индекс для сохранить следующий элемент и в конце цикла новый размер вектора.

код:

void erase(std::vector<int>& v, int num)
{
  size_t j = 0;
  for (size_t i = 0; i < v.size(); ++i) {
    if (v[i] != num) v[j++] = v[i];
  }
  // trim vector to new size
  v.resize(j);
}

В таком случае у вас нет недействительности итераторов, сложность O (n), а код очень краткий, и вам не нужно писать некоторые вспомогательные классы, хотя в некоторых случаях использование вспомогательных классов может принести пользу более гибкому коду.

Этот код не использует метод erase, но решает вашу задачу.

Используя чистый stl, вы можете сделать это следующим образом (это похоже на ответ Motti):

#include <algorithm>

void erase(std::vector<int>& v, int num) {
    vector<int>::iterator it = remove(v.begin(), v.end(), num);
    v.erase(it, v.end());
}

Ответ 4

В зависимости от того, почему вы это делаете, использование std:: set может быть лучше, чем std::vector.

Он позволяет каждому элементу встречаться только один раз. Если вы добавите его несколько раз, в любом случае будет только один экземпляр. Это сделает операцию стирания тривиальной. Операция стирания также будет иметь более низкую временную сложность, чем на векторе, однако добавление элементов на множестве будет медленнее, поэтому это может быть не слишком выгодным.

Это, конечно, не будет работать, если вы заинтересованы в том, сколько раз элемент добавлен в ваш вектор или порядок, в который были добавлены элементы.

Ответ 5

Вы можете начать с конца или обновить конец, когда элемент будет удален.

Он будет выглядеть примерно так:

void erase(std::vector<int>& myNumbers_in, int number_in)
    {
        std::vector<int>::iterator iter = myNumbers_in.begin();
        std::vector<int>::iterator endIter = myNumbers_in.end();
        for(; iter != endIter; ++iter)
        {
                if(*iter == number_in)
                {
                        myNumbers_in.erase(iter);
                        endIter = myNumbers_in.end();
                }
        }

    }