¿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(???);

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

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.


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

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

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

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.


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)

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


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

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


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

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


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.