Como faço para apagar um elemento de std :: vector <> pelo índice?


508

Eu tenho um std :: vector <int> e quero excluir o n-ésimo elemento. Como faço isso?

std::vector<int> vec;

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

vec.erase(???);

4
Considere usar um std :: deque que fornece inserção e exclusão nas duas extremidades.
Dario

40
Não, não considere usar o deque apenas porque você pode excluir um elemento, isso é um péssimo conselho. Existem várias razões pelas quais você pode querer usar deque ou vector. É verdade que excluir um elemento de um vetor pode ser caro - especialmente se o vetor for grande, mas não há razão para pensar que um deque seria melhor do que um vetor do exemplo de código que você acabou de postar.
coruja

6
Por exemplo, se você tem um aplicativo gráfico no qual exibe uma "lista" de itens em que você insere / remove itens interativamente, considere percorrer a lista de 50 a 100 vezes por segundo para exibi-los e adicione / remova alguns itens vezes a cada minuto. Portanto, implementar a "lista" como vetor provavelmente é uma opção melhor em termos de eficiência total.
Michel Billaud

Respostas:


705

Para excluir um único elemento, você pode fazer:

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

Ou, para excluir mais de um elemento de uma vez:

// Deletes the second through third elements (vec[1], vec[2])
vec.erase(vec.begin() + 1, vec.begin() + 3);

50
Observe também que binário nãooperator+ é necessariamente definido para iteradores em outros tipos de contêiner, como (você não pode fazer em um , você deve usarlist<T>::iteratorlist.begin() + 2std::liststd::advance para isso)
bobobobo

você está afirmando que o "+1" é o primeiro elemento myVector [0] ou a posição real myVector [1]
K - A toxicidade no SO está aumentando.

2
Com antecedência, você deve salvar o iterador em uma variável. Se você usar std :: next, poderá fazê-lo em uma linha: vec.erase (next (begin (vec), 123));
Dani #

8
Obrigado a todos que responderam. O que devemos pensar em um design de classe quando uma operação tão simples como excluir um elemento exige que você chegue ao StackOverflow?
Pierre

5
@ Pierre porque o índice numérico de um elemento em particular não é o modelo principal de acesso, é o iterador . Todas as funções que examinam os elementos de um contêiner usam os iteradores desse contêiner. Por exemplostd::find_if
Caleth 03/07

212

O método erase no std :: vector está sobrecarregado, por isso é provavelmente mais claro chamar

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

quando você deseja apagar apenas um único elemento.


3
Mas esse problema aparece, não importa quantos elementos você tenha.
Zyx 2000

15
se houver apenas um elemento, o índice é 0 e, portanto, você obtém vec.begin()qual é válido.
Anne Quinn

28
Eu gostaria que alguém tivesse mencionado que vec.erase(0)não funciona, mas vec.erase(vec.begin()+0)(ou sem +0) funciona. Caso contrário, eu não obter chamada de função correspondente, que é por isso que vim aqui
qrtLs

@qrtLs vec.erase(0)pode realmente compilar se 0passa a ser interpretada como a constante ponteiro 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, o que torna essa função melhor do que: template <typename T> void remove(std::vector<T>& vec, size_t pos) { vec.erase(vec.begin + pos); }eu não estou dizendo que qualquer uma é melhor, apenas pedindo por interesse pessoal e retornar o melhor resultado que essa pergunta poderia obter.

13
@ JoeyvG: Como a vector<T>::iteratoré um iterador de acesso aleatório, sua versão é boa e talvez um pouco mais clara. Mas a versão que Max postou deve funcionar muito bem se você alterar o recipiente para um outro que não suporta iteradores de acesso aleatório
Lily Ballard

2
Essa é a melhor resposta, pois também se aplica a outros formatos de contêiner. Você também pode usar std :: next ().
Bim

Abordagem muito melhor, pois não depende das partes internas do contêiner.
BartoszKP

std :: advance é necessário apenas se você achar que este não será um vetor, ou seja, uma lista. Mas como você especificou isso aqui, o operador + não seria mais simples? De acordo com este stackoverflow.com/questions/1668088/… , é possível um ganho de desempenho com o operador +
Neil McGill

14

O erasemétodo será usado de duas maneiras:

  1. Apagando um único elemento:

    vector.erase( vector.begin() + 3 ); // Deleting the fourth element
  2. Apagando vários elementos:

    vector.erase( vector.begin() + 3, vector.begin() + 5 ); // Deleting from fourth element to sixth element

7
Esta é uma resposta duplicada quase 7 anos após a resposta aceita. Por favor, não faça isso.
AlastairG

10

Na verdade, a erasefunção funciona para dois perfis:

  • Removendo um Único Elemento

    iterator erase (iterator position);
  • Removendo uma variedade de elementos

    iterator erase (iterator first, iterator last);

Como std :: vec.begin () marca o início do contêiner e, se quisermos excluir o i-ésimo elemento em nosso vetor, podemos usar:

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

Se você observar atentamente, vec.begin () é apenas um ponteiro para a posição inicial do nosso vetor e, ao adicionar o valor de i, ele aumenta o ponteiro para a posição i; portanto, podemos acessar o ponteiro para o i-ésimo elemento:

&vec[i]

Para que possamos escrever:

vec.erase(&vec[i]); // To delete the ith element

6
-1 A última linha não é compilada (pelo menos no VS2017). O código assume que vector :: iterator é implicitamente construtível a partir de um ponteiro bruto, o que não é exigido pelo padrão.
CuriousGeorge

1
Isto é verdade especialmente para iterators depuração
Nishant Singh

9

Se você tem um vetor não ordenado, pode aproveitar o fato de ele não ser ordenado e usar algo que vi de Dan Higgins no 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 a ordem da lista não importa, basta pegar o último elemento da lista e copiá-lo por cima do item que você deseja remover, em seguida, pop e exclua o último item.


Eu acho que essa é a melhor resposta se o vetor não for ordenado. Ele não se baseia na suposição de que iterator + indexvocê retornará a posição do iterador nesse índice, o que não é verdade para todos os contêineres iteráveis. Também é uma complexidade constante, em vez de linear, aproveitando o ponteiro de trás.
theferrit32

1
Isso precisa ser totalmente adicionado à lib std como unordered_removee unordered_remove_if… a menos que tenha sido e eu perdi, o que está acontecendo com mais frequência hoje em dia :)
Will Crawford

Se sugeriria usar atribuição de movimento ou troca em vez de uma atribuição de cópia.
Carsten S

std::removereordena o contêiner para que todos os elementos a serem removidos estejam no final, não é necessário fazê-lo manualmente dessa maneira, se você estiver usando o C ++ 17.
keith

@keith como std::removeajuda? A cppreference afirma que, mesmo no C ++ 17, todas as removesobrecargas exigem um predicado e nenhuma recebe um índice.
Paul Du Bois

4

Se você trabalha com vetores grandes (tamanho> 100.000) e deseja excluir muitos elementos, eu recomendaria fazer algo assim:

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

O código pega todos os números no vec que não podem ser divididos por 3 e o copia no vec2. Depois, copia o vec2 no vec. É bem rápido. Para processar 20.000.000 de elementos, esse algoritmo leva apenas 0,8 s!

Fiz a mesma coisa com o método erase, e leva muito e muito tempo:

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)

6
como isso responde a pergunta?
Regis Portalez

4
Interessante, mas não relevante para a questão!
Roddy

Um algoritmo no local não será mais rápido?
user202729

2
que de std :: remove_if (+ apagar)
Riad


3

Sugiro ler isso, pois acredito que é isso que você está procurando.https://en.wikipedia.org/wiki/Erase%E2%80%93remove_idiom

Se você usa por exemplo

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

você apagará o n-ésimo elemento do vetor, mas quando você apagar o segundo elemento, todos os outros elementos do vetor serão deslocados e o tamanho do vetor será -1. Isso pode ser um problema se você percorrer o vetor, pois o tamanho do vetor () está diminuindo. Se você tiver um problema como este, o link fornecido sugere o uso do algoritmo existente na biblioteca C ++ padrão. e "remover" ou "remover_se".

Espero que isso tenha ajudado


0

As respostas anteriores assumem que você sempre tem um índice assinado. Infelizmente, std::vectorusa size_typepara indexação e difference_typearitmética do iterador, para que não funcionem juntos se você tiver "-Wconversion" e amigos ativados. Esta é outra maneira de responder à pergunta, enquanto é capaz de lidar com assinados e não assinados:

Remover:

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

Pegar:

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

aqui está mais uma maneira de fazer isso, se você deseja excluir um elemento encontrando-o com seu valor em vetor, basta fazer isso em vetor.

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

isso removerá seu valor daqui. obrigado


0

Que tal agora?

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

o caminho mais rápido (para programação de concursos por complexidade de tempo () = constante)

pode apagar 100 milhões de itens em 1 segundo;

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

e maneira mais legível: vec.erase(vec.begin() + pos);


2
Isso é muito não portátil; funcionará com libstdc ++, mas não libc ++, e não com MSVC. vector<int>::iteratornão é necessariamente o mesmo queint *
Marshall Clow

2
É nojento, acho que vou mudar o libstdc ++ para impedir que ele funcione.
Jonathan Wakely
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.