¿Cómo borro un elemento de std :: vector <> por índice?

508

Tengo un std :: vector <int>, y quiero eliminar el enésimo elemento. ¿Cómo puedo hacer eso?

std::vector<int> vec;

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

vec.erase(???);
dau_man
fuente
44
Considere usar un std :: deque que proporciona inserción y eliminación en ambos extremos.
Dario
40
No, no considere usar deque solo porque desee eliminar un elemento, eso es realmente un mal consejo. Hay una gran cantidad de razones por las que es posible que desee utilizar deque o vector. Es cierto que eliminar un elemento de un vector puede ser costoso, especialmente si el vector es grande, pero no hay razón para pensar que una eliminación sería mejor que un vector del ejemplo de código que acaba de publicar.
Owl
66
Por ejemplo, si tiene una aplicación gráfica en la que muestra una "lista" de cosas donde inserta / elimina cosas de forma interactiva, considere que recorre la lista 50-100 veces por segundo para mostrarlas, y agrega / elimina algunas cosas veces cada minuto. Por lo tanto, implementar la "lista" como un vector es probablemente una mejor opción en términos de eficiencia total.
Michel Billaud

Respuestas:

706

Para eliminar un solo elemento, puede hacer:

std::vector<int> vec;

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

// Deletes the second element (vec[1])
vec.erase(vec.begin() + 1);

O, para eliminar más de un elemento a la vez:

// Deletes the second through third elements (vec[1], vec[2])
vec.erase(vec.begin() + 1, vec.begin() + 3);
mmmmmmmm
fuente
50
Tenga en cuenta que el binario nooperator+ está necesariamente definido para iteradores en otros tipos de contenedores, como (no puede hacerlo en un , debe usar para eso)list<T>::iteratorlist.begin() + 2std::liststd::advance
bobobobo
¿Está diciendo que "+1" es el primer elemento myVector [0] o la posición real myVector [1]
K - La toxicidad en SO está creciendo.
2
Con anticipación, debe guardar el iterador en una variable. Si usa std :: next puede hacerlo en una línea: vec.erase (next (begin (vec), 123));
dani
8
Gracias a todos los que respondieron. ¿Qué debemos pensar de un diseño de clase cuando una operación tan simple como eliminar un elemento requiere que uno venga a StackOverflow?
Pierre
55
@Pierre porque el índice numérico de un elemento en particular no es el modelo principal de acceso, sí lo es el iterador . Todas las funciones que miran los elementos de un contenedor utilizan los iteradores de ese contenedor. Por ejemplostd::find_if
Caleth
212

El método de borrado en std :: vector está sobrecargado, por lo que probablemente sea más claro llamar

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

cuando solo quieres borrar un solo elemento.

CodeBuddy
fuente
3
Pero ese problema aparece sin importar cuántos elementos tenga.
Zyx 2000
15
si solo hay un elemento, el índice es 0, y así obtienes vec.begin()cuál es válido.
Anne Quinn el
28
Desearía que alguien hubiera mencionado que vec.erase(0)no funciona, pero vec.erase(vec.begin()+0)(o sin +0) sí. De lo contrario, no recibo una llamada de función coincidente, por eso vine aquí
qrtLs
@qrtLs en vec.erase(0)realidad puede compilarse si 0se interpreta como la constante de puntero nulo ...
LF
57
template <typename T>
void remove(std::vector<T>& vec, size_t pos)
{
    std::vector<T>::iterator it = vec.begin();
    std::advance(it, pos);
    vec.erase(it);
}
Max
fuente
2
Max, lo que hace que esa función sea mejor que: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }No estoy diciendo que ninguno sea mejor, simplemente preguntando por interés personal y para devolver el mejor resultado que esta pregunta podría obtener.
13
@JoeyvG: Dado que a vector<T>::iteratores un iterador de acceso aleatorio, su versión está bien y quizás un poco más clara. Pero la versión que publicó Max debería funcionar bien si cambia el contenedor a otro que no sea compatible con iteradores de acceso aleatorio
Lily Ballard
2
Esta es la mejor respuesta, ya que también se aplica a otros formatos de contenedor. También puede usar std :: next ().
Bim
Enfoque mucho mejor ya que no depende de la parte interna del contenedor.
BartoszKP
std :: advance solo es necesario si cree que esto no será un vector, es decir, una lista. Pero como lo ha especificado aquí, ¿operador + no sería más simple? De acuerdo a esto stackoverflow.com/questions/1668088/... hay una posible ganancia en el rendimiento con el operador +
Neil McGill
14

El erasemétodo se usará de dos maneras:

  1. Borrar un solo elemento:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
  2. Borrado de rango de elementos:

    vector.erase( vector.begin() + 3, vector.begin() + 5 ); // Deleting from fourth element to sixth element
Eswaran Pandi
fuente
77
Esta es una respuesta duplicada casi 7 años después de la respuesta aceptada. Por favor no hagas esto.
AlastairG
10

En realidad, la erasefunción funciona para dos perfiles:

  • Eliminar un solo elemento

    iterator erase (iterator position);
  • Eliminar una gama de elementos

    iterator erase (iterator first, iterator last);

Dado que std :: vec.begin () marca el inicio del contenedor y si queremos eliminar el elemento i-ésimo en nuestro vector, podemos usar:

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

Si observa de cerca, vec.begin () es solo un puntero a la posición inicial de nuestro vector y al agregarle el valor de i incrementa el puntero a la posición i, por lo que podemos acceder al puntero al elemento i-ésimo de la siguiente manera:

&vec[i]

Entonces podemos escribir:

vec.erase(&vec[i]); // To delete the ith element
Varun Garg
fuente
66
-1 La última línea no se compila (al menos en VS2017). El código asume que vector :: iterator es implícitamente construible a partir de un puntero sin procesar, que no es requerido por el estándar.
CuriousGeorge
1
Esto es cierto especialmente para los iteradores de depuración
Nishant Singh
9

Si tiene un vector desordenado, puede aprovechar el hecho de que está desordenado y usar algo que vi de Dan Higgins en 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;
}

Como el orden de la lista no importa, solo tome el último elemento de la lista y cópielo sobre la parte superior del elemento que desea eliminar, luego haga estallar y elimine el último elemento.

Clay J
fuente
Creo que esta es la mejor respuesta si el vector no está ordenado. No se basa en el supuesto de que iterator + indexrealmente le devolverá la posición del iterador en ese índice, lo que no es cierto para todos los contenedores iterables. También es una complejidad constante en lugar de lineal al aprovechar el puntero posterior.
theferrit32
1
Esto debe agregarse totalmente a la biblioteca estándar como unordered_removey unordered_remove_if... a menos que lo haya sido y lo haya extrañado, lo que sucede cada vez más a menudo en estos días :)
Will Crawford
Sugeriría usar mover-asignación o intercambio en lugar de una copia-asignación.
Carsten S
std::removereordena el contenedor para que todos los elementos que se eliminarán estén al final, no es necesario hacerlo manualmente si está usando C ++ 17.
keith
@keith ¿cómo std::removeayuda? cppreference afirma que incluso en C ++ 17, todas las removesobrecargas requieren un predicado y ninguna toma un índice.
Paul Du Bois
4

Si trabaja con vectores grandes (tamaño> 100,000) y desea eliminar muchos elementos, le recomendaría hacer algo como esto:

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

El código toma cada número en vec que no se puede dividir entre 3 y lo copia en vec2. Luego copia vec2 en vec. Es bastante rápido ¡Para procesar 20,000,000 elementos, este algoritmo solo toma 0.8 segundos!

Hice lo mismo con el método de borrado, y lleva mucho, mucho tiempo:

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)
Fabian
fuente
66
¿Cómo responde esto a la pregunta?
Regis Portalez
44
¡Interesante, pero no relevante para la pregunta!
Roddy
¿No será un algoritmo en el lugar más rápido?
user202729
2
eso es std :: remove_if (+ erase)
RiaD
3

Sugiero leer esto ya que creo que eso es lo que estás buscando. https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

Si usas por ejemplo

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

borrará el enésimo elemento del vector, pero cuando borre el segundo elemento, todos los demás elementos del vector se desplazarán y el tamaño del vector será -1. Esto puede ser un problema si recorre el vector ya que el tamaño del vector () está disminuyendo. Si tiene un problema como este, el enlace proporcionado sugiere utilizar el algoritmo existente en la biblioteca estándar de C ++. y "eliminar" o "eliminar_si".

Espero que esto haya ayudado

explorador
fuente
0

Las respuestas anteriores suponen que siempre tiene un índice firmado. Lamentablemente, los std::vectorusos size_typepara la indexación, ydifference_type aritmética de iteradores, por lo que no funcionan juntos si tiene "-Wconversion" y amigos habilitados. Esta es otra forma de responder la pregunta, al tiempo que puede manejar tanto con signo como sin signo:

Para eliminar:

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

Tomar:

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;
}
Rian Quinn
fuente
0

Aquí hay una forma más de hacer esto si desea eliminar un elemento encontrando esto con su valor en vector, solo necesita hacer esto en vector.

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

eliminará tu valor de aquí. Gracias

meenachinmay
fuente
0

¿Qué tal esto?

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);
}
def
fuente
-4

la forma más rápida (para programar concursos por complejidad de tiempo () = constante)

puede borrar un elemento de 100M en 1 segundo;

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

y la forma más legible: vec.erase(vec.begin() + pos);

R.hatam
fuente
2
Esto es muy no portátil; funcionará con libstdc ++, pero no con libc ++, y no con MSVC. vector<int>::iteratorno es necesariamente lo mismo queint *
Marshall Clow
2
Es asqueroso, creo que cambiaré libstdc ++ para evitar que funcione.
Jonathan Wakely