Как стереть элемент из std :: vector ‹› по индексу?

У меня есть std :: vector ‹int>, и я хочу удалить n-й элемент. Как я могу это сделать?

std::vector<int> vec;

vec.push_back(6);
vec.push_back(-17);
vec.push_back(12);

vec.erase(???);

person dau_man    schedule 17.05.2009    source источник
comment
Рассмотрите возможность использования std :: deque, который обеспечивает вставку и удаление с обоих концов.   -  person Dario    schedule 17.05.2009
comment
Нет, не рассматривайте использование deque только потому, что вы можете захотеть удалить элемент, это действительно плохой совет. Существует множество причин, по которым вы можете захотеть использовать deque или vector. Это правда, что удаление элемента из вектора может быть дорогостоящим - особенно, если вектор большой, но нет причин думать, что двухсторонняя очередь будет лучше вектора из примера кода, который вы только что опубликовали.   -  person Owl    schedule 02.04.2017
comment
Например, если у вас есть графическое приложение, в котором вы отображаете список вещей, в которые вы вставляете / удаляете элементы в интерактивном режиме, рассмотрите, что вы просматриваете список 50-100 раз в секунду, чтобы отобразить их, и вы добавляете / удаляете элементы по несколько раз каждый минута. Таким образом, реализация списка в виде вектора, вероятно, является лучшим вариантом с точки зрения общей эффективности.   -  person Michel Billaud    schedule 28.05.2017
comment
Я рекомендую std :: vector.erase (...), что я тоже предпочитаю - вы можете удалить либо один элемент, либо диапазон.   -  person    schedule 30.10.2020
comment
Читая эти ответы, я думаю, что лучший способ стереть данные из массива - использовать другой язык.   -  person sudo    schedule 13.11.2020


Ответы (16)


Чтобы удалить один элемент, вы можете:

std::vector<int> vec;

vec.push_back(6);
vec.push_back(-17);
vec.push_back(12);

// Deletes the second element (vec[1])
vec.erase(std::next(vec.begin()));

Или, чтобы удалить сразу несколько элементов:

// Deletes the second through third elements (vec[1], vec[2])
vec.erase(std::next(vec.begin(), 1), std::next(vec.begin(), 3));
person mmmmmmmm    schedule 17.05.2009
comment
Обратите внимание, что двоичный operator+ не обязательно определен для итераторов в других типах контейнеров, например list<T>::iterator (вы не можете использовать list.begin() + 2 для std::list, вы должны использовать _ 5_ для этого) - person bobobobo; 15.03.2013
comment
вы утверждаете, что +1 - это первый элемент myVector [0] или фактическая позиция myVector [1] - person basickarl; 19.09.2014
comment
it + 1 - это элемент с идентификатором 1, например, контейнер [1]. первый элемент +0. См. Комментарий ниже ... - person Nick; 12.04.2016
comment
Предварительно необходимо сохранить итератор в переменной. Если вы используете std :: next, вы можете сделать это одной строкой: vec.erase (next (begin (vec), 123)); - person dani; 05.10.2016
comment
Спасибо всем, кто ответил. Что мы должны думать о дизайне класса, когда такая простая операция, как удаление элемента, требует перехода в StackOverflow? - person Pierre; 28.01.2018
comment
что такое begin()? Почему не просто 1? - person user25; 10.04.2018
comment
@ user25 erase принимает вектор ‹T› :: iterator, а не int. begin () - это итератор в начале вектора или element [0]. Добавляя к нему, мы увеличиваем его на количество элементов, необходимых для поиска элемента, который мы хотим удалить. - person Force Gaia; 11.06.2018
comment
@Pierre, потому что числовой индекс определенного элемента не является основной моделью доступа, а итератор. Все функции, которые просматривают элементы контейнера, используют итераторы этого контейнера. Например. std::find_if - person Caleth; 03.07.2018
comment
@Caleth, да, но std :: vector все еще может предоставить метод для этого очень распространенного варианта использования. Все кричат ​​о контейнерах Qt, но в QList, например, есть removeOne (), что несложно по сравнению с уродством std :: vector. - person Richard W; 21.01.2021
comment
@RichardW Давайте не будем превращать красивое std::vector в кошмарное std::string. - person Evg; 17.07.2021

Метод стирания в std :: vector перегружен, поэтому, вероятно, проще вызвать

vec.erase(vec.begin() + index);

когда вы хотите стереть только один элемент.

person CodeBuddy    schedule 17.05.2009
comment
Но эта проблема возникает независимо от того, сколько элементов у вас есть. - person Zyx 2000; 01.09.2014
comment
если есть только один элемент, индекс равен 0, и поэтому вы получаете vec.begin(), что является допустимым. - person Anne Quinn; 27.01.2015
comment
Хотелось бы, чтобы кто-нибудь упомянул, что vec.erase(0) не работает, а vec.erase(vec.begin()+0) (или без +0) работает. В противном случае я не получу соответствующего вызова функции, поэтому я пришел сюда - person qrtLs; 15.02.2016
comment
@qrtLs vec.erase(0) может фактически компилироваться, если 0 интерпретируется как константа нулевого указателя ... - person L. F.; 24.07.2019
comment
Функция @qrtLs erase () принимает в качестве аргумента тип итератора; поскольку 0 не является итератором, это приведет к ошибке компилятора из-за отсутствия соответствующего вызова функции. - person dixit_chandra; 25.05.2021

Метод erase будет использоваться двумя способами:

  1. Удаление отдельного элемента:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
    
  2. Стирание ряда элементов:

    vector.erase( vector.begin() + 3, vector.begin() + 5 ); // Deleting from fourth element to sixth element
    
person Eswaran Pandi    schedule 04.05.2016
comment
Это повторяющийся ответ почти через 7 лет после принятого ответа. Пожалуйста, не делай этого. - person AlastairG; 26.06.2019
comment
@AlastairG Этот ответ намного короче и яснее, чем исходный ответ, хотя технически он может быть просто редактированием вместо этого (такое редактирование может противоречить пожеланиям OP исходного ответа) - person Nicholas Pipitone; 27.09.2020

Фактически функция erase работает для двух профилей:

  • Удаление одного элемента

    iterator erase (iterator position);
    
  • Удаление ряда элементов

    iterator erase (iterator first, iterator last);
    

Поскольку std :: vec.begin () отмечает начало контейнера, и если мы хотим удалить i-й элемент в нашем векторе, мы можем использовать:

vec.erase(vec.begin() + index);

Если вы присмотритесь, vec.begin () - это просто указатель на начальную позицию нашего вектора, и добавление к нему значения i увеличивает указатель на позицию i, поэтому вместо этого мы можем получить доступ к указателю на i-й элемент следующим образом:

&vec[i]

Итак, мы можем написать:

vec.erase(&vec[i]); // To delete the ith element
person Varun Garg    schedule 20.05.2016
comment
-1 Последняя строка не компилируется (по крайней мере, в VS2017). В коде предполагается, что vector :: iterator неявно конструируется из необработанного указателя, что не требуется стандартом. - person CuriousGeorge; 11.04.2018
comment
Это особенно актуально для итераторов отладки. - person Nishant Singh; 09.05.2019

Если у вас неупорядоченный вектор, вы можете воспользоваться тем фактом, что он неупорядочен, и использовать то, что я видел у Дэна Хиггинса из CPPCON.

template< typename TContainer >
static bool EraseFromUnorderedByIndex( TContainer& inContainer, size_t inIndex )
{
    if ( inIndex < inContainer.size() )
    {
        if ( inIndex != inContainer.size() - 1 )
            inContainer[inIndex] = inContainer.back();
        inContainer.pop_back();
        return true;
    }
    return false;
}

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

person Clay J    schedule 18.09.2017
comment
Думаю, это лучший ответ, если вектор неупорядочен. Он не полагается на предположение, что iterator + index фактически вернет вам позицию итератора по этому индексу, что не верно для всех повторяемых контейнеров. Это также постоянная сложность вместо линейной за счет использования обратного указателя. - person theferrit32; 15.03.2018
comment
Это совершенно необходимо добавить в std lib как unordered_remove и unordered_remove_if ... если только это не было, и я пропустил это, что в наши дни происходит все чаще и чаще :) - person Will Crawford; 12.03.2020
comment
If предложит использовать присвоение перемещения или замену вместо присваивания копии. - person Carsten S; 12.03.2020
comment
std::remove переупорядочивает контейнер так, чтобы все удаляемые элементы находились в конце, нет необходимости делать это вручную, если вы используете C ++ 17. - person keith; 20.04.2020
comment
@keith как std::remove помогает? cppreference утверждает, что даже в C ++ 17 все remove перегрузки требуют предиката, и ни одна из них не принимает индекс. - person Paul Du Bois; 27.05.2020

Некоторым это может показаться очевидным, но уточним приведенные выше ответы:

Если вы удаляете std::vector элементы, используя erase в цикле по всему вектору, вы должны обрабатывать свой вектор в обратном порядке, то есть используя

for (int i = v.size() - 1; i >= 0; i--)

вместо (классический)

for (int i = 0; i < v.size(); i++)

Причина в том, что на индексы влияет erase, поэтому, если вы удалите 4-й элемент, бывший 5-й элемент теперь станет новым 4-м элементом, и он не будет обрабатываться вашим циклом, если вы выполняете i++.

Ниже приведен простой пример, иллюстрирующий это, где я хочу удалить все элементы odds вектора int;

#include <iostream>
#include <vector>

using namespace std;

void printVector(const vector<int> &v)
{
    for (size_t i = 0; i < v.size(); i++)
    {
        cout << v[i] << " ";
    }
    cout << endl;
}

int main()
{    
    vector<int> v1, v2;
    for (int i = 0; i < 10; i++)
    {
        v1.push_back(i);
        v2.push_back(i);
    }

    // print v1
    cout << "v1: " << endl;
    printVector(v1);
    
    cout << endl;
    
    // print v2
    cout << "v2: " << endl;
    printVector(v2);
    
    // Erase all odd elements
    cout << "--- Erase odd elements ---" << endl;
    
    // loop with decreasing indices
    cout << "Process v2 with decreasing indices: " << endl;
    for (int i = v2.size() - 1; i >= 0; i--)
    {
        if (v2[i] % 2 != 0)
        {
            cout << "# ";
            v2.erase(v2.begin() + i);
        }
        else
        {
            cout << v2[i] << " ";
        }
    }
    cout << endl;
    cout << endl;
    
    // loop with increasing indices
    cout << "Process v1 with increasing indices: " << endl;
    for (int i = 0; i < v1.size(); i++)
    {
        if (v1[i] % 2 != 0)
        {
            cout << "# ";
            v1.erase(v1.begin() + i);
        }
        else
        {
            cout << v1[i] << " ";
        }
    }
    
    
    return 0;
}

Выход:

v1:
0 1 2 3 4 5 6 7 8 9

v2:
0 1 2 3 4 5 6 7 8 9
--- Erase odd elements ---
Process v2 with decreasing indices:
# 8 # 6 # 4 # 2 # 0

Process v1 with increasing indices:
0 # # # # #

Обратите внимание, что во второй версии с увеличивающимися индексами четные числа не отображаются, так как они пропускаются из-за i++

person Pierre Baret    schedule 26.08.2020

Сотрите элемент с индексом:

vec.erase(vec.begin() + index);

Удалите элемент со значением:

vec.erase(find(vec.begin(),vec.end(),value));
person Legend    schedule 02.09.2020
comment
Пожалуйста, сделайте более очевидным дополнительное понимание этого ответа по сравнению с другими существующими более старыми ответами и ответами, за которые проголосовали. - person Yunnosch; 07.09.2020

Если вы работаете с большими векторами (размер> 100000) и хотите удалить много элементов, я бы рекомендовал сделать что-то вроде этого:

int main(int argc, char** argv) {

    vector <int> vec;
    vector <int> vec2;

    for (int i = 0; i < 20000000; i++){
        vec.push_back(i);}

    for (int i = 0; i < vec.size(); i++)
    {
        if(vec.at(i) %3 != 0)
            vec2.push_back(i);
    }

    vec = vec2;
    cout << vec.size() << endl;
}

Код берет каждое число в vec, которое нельзя разделить на 3, и копирует его в vec2. После этого он копирует vec2 в vec. Это довольно быстро. Для обработки 20 000 000 элементов этот алгоритм занимает всего 0,8 секунды!

Я проделал то же самое с методом стирания, и на это уходит много-много времени:

Erase-Version (10k elements)  : 0.04 sec
Erase-Version (100k elements) : 0.6  sec
Erase-Version (1000k elements): 56   sec
Erase-Version (10000k elements): ...still calculating (>30 min)
person Fabian    schedule 12.05.2016
comment
как это отвечает на вопрос? - person Regis Portalez; 12.05.2016
comment
Интересно, но не к вопросу! - person Roddy; 09.06.2016
comment
Не будет ли алгоритм на месте быстрее? - person user202729; 21.09.2016
comment
это std :: remove_if (+ стереть) - person RiaD; 05.04.2017

Чтобы удалить элемент, используйте следующий способ:

// declaring and assigning array1 
std:vector<int> array1 {0,2,3,4};

// erasing the value in the array
array1.erase(array1.begin()+n);

Чтобы получить более широкий обзор, посетите: http://www.cplusplus.com/reference/vector/vector/erase/

person cammando    schedule 18.06.2016
comment
Рассмотрите возможность использования cppreference. См. это, this и т. д. - person L. F.; 07.02.2019

Предлагаю прочитать это, поскольку считаю, что это именно то, что вы ищете. https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

Если вы используете, например,

 vec.erase(vec.begin() + 1, vec.begin() + 3);

вы удалите n-й элемент вектора, но когда вы удалите второй элемент, все остальные элементы вектора будут сдвинуты, а размер вектора будет равен -1. Это может быть проблемой, если вы перебираете вектор, поскольку размер вектора () уменьшается. Если у вас есть проблема, как эта, предоставленная ссылка предлагает использовать существующий алгоритм в стандартной библиотеке C ++. и «удалить» или «удалить_если».

Надеюсь, что это помогло

person explorer    schedule 11.07.2019

Предыдущие ответы предполагают, что у вас всегда есть индекс со знаком. К сожалению, std::vector использует size_type для индексации и difference_type для арифметики итератора, поэтому они не работают вместе, если у вас включен "-Wconversion" и друзья. Это еще один способ ответить на вопрос, имея возможность обрабатывать как подписанные, так и неподписанные:

Удалять:

template<class T, class I, class = typename std::enable_if<std::is_integral<I>::value>::type>
void remove(std::vector<T> &v, I index)
{
    const auto &iter = v.cbegin() + gsl::narrow_cast<typename std::vector<T>::difference_type>(index);
    v.erase(iter);
}

Принять:

template<class T, class I, class = typename std::enable_if<std::is_integral<I>::value>::type>
T take(std::vector<T> &v, I index)
{
    const auto &iter = v.cbegin() + gsl::narrow_cast<typename std::vector<T>::difference_type>(index);

    auto val = *iter;
    v.erase(iter);

    return val;
}
person Rian Quinn    schedule 25.10.2016

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

vector<int> ar(n);
ar.erase(remove(ar.begin(), ar.end()), (place your value here from vector array));

он удалит ваше значение отсюда. Благодарность

person meenachinmay    schedule 23.08.2018

Как насчет этого?

void squeeze(vector<int> &v)
{
    int j = 0;
    for (int i = 1; i < v.size(); i++)
        if (v[i] != v[j] && ++j != i)
            v[j] = v[i];
    v.resize(j + 1);
}
person def    schedule 24.06.2019

если вам нужно стереть элемент внутри цикла for, сделайте следующее:

for(int i = 0; i < vec.size(); i++){

    if(condition)
        vec.erase(vec.begin() + i);

}
person Disembleergon 48    schedule 12.03.2021

самый быстрый способ (для олимпиад по программированию по временной сложности () = константа)

может стереть 100M элементов за 1 секунду;

    vector<int> it = (vector<int>::iterator) &vec[pos];
    vec.erase(it);

и наиболее читаемый способ: vec.erase(vec.begin() + pos);

person R.hatam    schedule 26.04.2019
comment
Это очень непереносимо; он будет работать с libstdc ++, но не с libc ++ и не с MSVC. vector<int>::iterator не обязательно то же самое, что int * - person Marshall Clow; 18.06.2019
comment
Противно, думаю, изменю libstdc ++, чтобы она перестала работать. - person Jonathan Wakely; 18.06.2019

person    schedule
comment
Макс, что делает эту функцию лучше, чем: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); } Я не говорю, что то и другое лучше, просто спрашиваю из личного интереса и для того, чтобы получить лучший результат, который может получить этот вопрос. - person ; 12.09.2012
comment
@JoeyvG: Поскольку vector<T>::iterator является итератором с произвольным доступом, ваша версия в порядке и, возможно, немного понятнее. Но версия, которую опубликовал Макс, должна работать нормально, если вы измените контейнер на другой, который не поддерживает итераторы с произвольным доступом. - person Lily Ballard; 12.09.2012
comment
Это лучший ответ, поскольку он применим и к другим форматам контейнеров. Вы также можете использовать std :: next (). - person Bim; 23.12.2016
comment
Намного лучший подход, поскольку он не полагается на внутреннюю часть контейнера. - person BartoszKP; 20.05.2020
comment
std :: advance нужен только в том случае, если вы думаете, что это не будет вектор, то есть список. Но, как вы указали здесь, не будет ли оператор + проще? Согласно этому stackoverflow .com / questions / 1668088 / возможен прирост производительности с оператором + - person Neil McGill; 08.06.2020