Como descobrir se um item está presente em um std :: vector?

616

Tudo o que quero fazer é verificar se existe um elemento no vetor ou não, para que eu possa lidar com cada caso.

if ( item_present )
   do_this();
else
   do_that();
Joan Venge
fonte
2
busca em um vetor é muito lento desde que você tem que olhar para cada elemento do vector de modo considerar o uso de um mapa se você está fazendo um monte de pesquisas
naumcho
7
@naumcho: Se o vetor é classificado, há sempre uma pesquisa binária, conforme publicado abaixo. Isso o torna tão rápido quanto um mapa e, se você estiver armazenando apenas valores (não mapas de chave / valor), utilizará muito menos memória.
21119 Adam Hawes
4
mapas certamente não são a melhor escolha, mas usar set pode ser útil. Se você precisar de tempo de pesquisa O (1), hash_set é o caminho a seguir.
Philipp
Um soberbo resposta presente em uma pergunta duplicado: stackoverflow.com/a/3451045/472647
CodeMouse92
1
Se você estiver procurando várias vezes por números diferentes, uma tabela de hash seria mais eficiente.
NL628

Respostas:

915

Você pode usar std::findem <algorithm>:

#include <vector>
vector<int> vec; 
//can have other data types instead of int but must same datatype as item 
std::find(vec.begin(), vec.end(), item) != vec.end()

Isso retorna um bool ( truese presente, falsecaso contrário). Com o seu exemplo:

#include <algorithm>
#include <vector>

if ( std::find(vec.begin(), vec.end(), item) != vec.end() )
   do_this();
else
   do_that();
MSN
fonte
216
Não vejo como count () pode ser mais rápido que find (), pois find () pára assim que um elemento é encontrado, enquanto count () sempre precisa varrer toda a sequência.
Éric Malenfant
114
Não se esqueça de #include <algorithm>ou então você pode obter erros muito estranhos como 'não consegue encontrar função correspondente no std namespace'
rustyx
80
Não incomodou ninguém que, apesar de o STL ser "orientado a objetos", .find()ainda não é uma função membro std::vector, como seria de esperar? Eu me pergunto se isso é de alguma forma uma conseqüência do modelo.
bobobobo
71
@obobobo: OOP não tem nada a ver com membros versus não-membros. E existe uma ampla escola de pensamento de que, se algo não precisa ser um membro, ou quando não dá nenhuma vantagem quando implementado como um membro, não deve ser um membro; std::vector<>::find()não daria nenhuma vantagem, nem é necessário; portanto, não, não deve ser um membro. Veja também en.wikipedia.org/wiki/Coupling_%28computer_programming%29
Sebastian Mach
36
@phresnel Eu diria que "quando não dá nenhuma vantagem quando implementado como membro" é falso neste caso. A vantagem é uma interface simplificada e clara. Por exemplo: mvec.find(key) != mvec.cend()é preferível std::find(mvec.cbegin(), mvec.cend(), key) != mvec.cend().
swalog
113

Como outros já disseram, use o STL findou find_iffunções. Mas se você está procurando em grandes vetores e isso afeta o desempenho, você pode querer classificar seu vetor e use as binary_search, lower_boundou upper_boundalgoritmos.

Brian Neal
fonte
3
Boa resposta! Encontrar é sempre o (n). lower_bound é o (log (n)) se usado com iteradores de acesso aleatório.
Stephen Edmonds
30
A classificação é O (nlogn), portanto, só vale a pena se você estiver fazendo mais que pesquisas O (logn).
Liori
7
@ liori True Depende dos seus padrões de uso. Se você precisar classificá-lo apenas uma vez, faça várias pesquisas repetidamente para salvar você.
Brian Neal
1
@ Brian Neal, a classificação de um vetor grande vale a pena se houver muitas pesquisas de elementos nele. Classificando será O (nlogn) e O (n) será melhor se a pessoa tem que encontrar um elemento apenas uma vez :)
Swapnil B.
47

Use find do cabeçalho do algoritmo de stl. Ilustrei seu uso com o tipo int. Você pode usar qualquer tipo que quiser, desde que possa comparar a igualdade (sobrecarga == se for necessário para sua classe personalizada).

#include <algorithm>
#include <vector>

using namespace std;
int main()
{   
    typedef vector<int> IntContainer;
    typedef IntContainer::iterator IntIterator;

    IntContainer vw;

    //...

    // find 5
    IntIterator i = find(vw.begin(), vw.end(), 5);

    if (i != vw.end()) {
        // found it
    } else {
        // doesn't exist
    }

    return 0;
}
m-sharp
fonte
2
Dependendo das necessidades do OP, find_if () também pode ser apropriado. Permite pesquisar usando um predicado arbitrário em vez de igualdade.
Éric Malenfant
Ops, seu comentário foi tarde demais. A resposta que eu dei também menciona find_if.
Frank
39

Se seu vetor não for solicitado, use a abordagem sugerida pelo MSN:

if(std::find(vector.begin(), vector.end(), item)!=vector.end()){
      // Found the item
}

Se seu vetor for encomendado, use o método binary_search sugerido por Brian Neal:

if(binary_search(vector.begin(), vector.end(), item)){
     // Found the item
}

a pesquisa binária produz O (log n) pior desempenho, o que é muito mais eficiente que a primeira abordagem. Para usar a pesquisa binária, você pode usar qsort para classificar o vetor primeiro e garantir que ele seja ordenado.

lua cheia
fonte
3
Você não quer dizer std::sort? qsorté muito ineficiente em vetores .... ver: stackoverflow.com/questions/12308243/...
Jason R. Mick
1
A pesquisa binária terá melhor desempenho em contêineres maiores, mas para contêineres pequenos é provável que uma pesquisa linear simples seja tão rápida ou mais rápida.
BillT
21

Eu uso algo assim ...

#include <algorithm>


template <typename T> 
const bool Contains( std::vector<T>& Vec, const T& Element ) 
{
    if (std::find(Vec.begin(), Vec.end(), Element) != Vec.end())
        return true;

    return false;
}

if (Contains(vector,item))
   blah
else
   blah

... dessa forma, é realmente claro e legível. (Obviamente, você pode reutilizar o modelo em vários locais).

Andy Krouwel
fonte
e você pode fazê-lo funcionar para listas ou vetores usando 2 typenames
Erik Aronesty
@ErikAronesty, você pode usar 1 argumento de modelo se usar value_typeo contêiner para o tipo de elemento. Eu adicionei uma resposta como esta.
Martin Broadhurst
13

No C ++ 11 você pode usar any_of. Por exemplo, se for um vector<string> v;então:

if (any_of(v.begin(), v.end(), bind(equal_to<string>(), _1, item)))
   do_this();
else
   do_that();

Como alternativa, use uma lambda:

if (any_of(v.begin(), v.end(), [&](const std::string& elem) { return elem == item; }))
   do_this();
else
   do_that();
Deqing
fonte
1
bind1ste bind2ndforam descontinuados desde C ++ 11 e completamente removidos em C ++ 17. Use bindcom placeholderse / ou lambdas.
andreee
11

Aqui está uma função que funcionará para qualquer contêiner:

template <class Container> 
const bool contains(const Container& container, const typename Container::value_type& element) 
{
    return std::find(container.begin(), container.end(), element) != container.end();
}

Observe que você pode se safar com 1 parâmetro de modelo porque pode extrair o value_typedo Container. Você precisa do typenameporque Container::value_typeé um nome dependente .

Martin Broadhurst
fonte
5
Observe que isso às vezes é um pouco amplo demais - funcionaria para std :: set por exemplo, mas apresentava um desempenho terrível comparado à função de membro find (). Eu achei melhor adicionar uma especialização para os recipientes com uma pesquisa mais rápida (set / mapa, unordered_ *)
Andy Krouwel
10

Lembre-se de que, se você estiver fazendo muitas pesquisas, existem contêineres STL que são melhores para isso. Não sei qual é a sua aplicação, mas vale a pena considerar contêineres associativos como std :: map.

std :: vector é o contêiner de escolha, a menos que você tenha uma razão para outra e as pesquisas por valor possam ser essa.

David Thornley
fonte
Mesmo com pesquisas por valor, o vetor pode ser uma boa escolha, desde que seja classificado e você use binary_search, lower_bound ou upper_bound. Se o conteúdo do contêiner for alterado entre pesquisas, o vetor não será muito bom devido à necessidade de classificar novamente.
Renze de Waal 20/02/09
8

Use a função de localização STL .

Lembre-se de que também existe uma função find_if , que você pode usar se sua pesquisa for mais complexa, ou seja, se você não estiver apenas procurando por um elemento, mas, por exemplo, desejar ver se há um elemento que atenda a um determinado condição, por exemplo, uma sequência que começa com "abc". ( find_ifdaria a você um iterador que aponta para o primeiro elemento desse tipo).

Frank
fonte
7

Com o boost você pode usar any_of_equal:

#include <boost/algorithm/cxx11/any_of.hpp>

bool item_present = boost::algorithm::any_of_equal(vector, element);
Mikhail
fonte
5

Você pode tentar este código:

#include <algorithm>
#include <vector>

// You can use class, struct or primitive data type for Item
struct Item {
    //Some fields
};
typedef std::vector<Item> ItemVector;
typedef ItemVector::iterator ItemIterator;
//...
ItemVector vtItem;
//... (init data for vtItem)
Item itemToFind;
//...

ItemIterator itemItr;
itemItr = std::find(vtItem.begin(), vtItem.end(), itemToFind);
if (itemItr != vtItem.end()) {
    // Item found
    // doThis()
}
else {
    // Item not found
    // doThat()
}
TrungTN
fonte
3

Você pode usar a findfunção, encontrada no stdespaço para nome, ie std::find. Você passa a std::findfunção begine o enditerador do vetor que deseja pesquisar, junto com o elemento que você está procurando e compara o iterador resultante ao final do vetor para ver se eles correspondem ou não.

std::find(vector.begin(), vector.end(), item) != vector.end()

Você também pode desreferenciar esse iterador e usá-lo normalmente, como qualquer outro iterador.

TankorSmash
fonte
3

Você pode usar count também. Ele retornará o número de itens presentes em um vetor.

int t=count(vec.begin(),vec.end(),item);
Aditya
fonte
11
findé mais rápido que count, porque não continua contando após a primeira partida.
Camille Goudeseune 16/08/2015
2

Se você quiser encontrar uma string em um vetor:

    struct isEqual
{
    isEqual(const std::string& s): m_s(s)
    {}

    bool operator()(OIDV* l)
    {
        return l->oid == m_s;
    }

    std::string m_s;
};
struct OIDV
{
    string oid;
//else
};
VecOidv::iterator itFind=find_if(vecOidv.begin(),vecOidv.end(),isEqual(szTmp));
Gank
fonte
2

Outro exemplo usando operadores C ++.

#include <vector>
#include <algorithm>
#include <stdexcept>

template<typename T>
inline static bool operator ==(const std::vector<T>& v, const T& elem)
{
  return (std::find(v.begin(), v.end(), elem) != v.end());
}

template<typename T>
inline static bool operator !=(const std::vector<T>& v, const T& elem)
{
  return (std::find(v.begin(), v.end(), elem) == v.end());
}

enum CODEC_ID {
  CODEC_ID_AAC,
  CODEC_ID_AC3,
  CODEC_ID_H262,
  CODEC_ID_H263,
  CODEC_ID_H264,
  CODEC_ID_H265,
  CODEC_ID_MAX
};

void main()
{
  CODEC_ID codec = CODEC_ID_H264;
  std::vector<CODEC_ID> codec_list;

  codec_list.reserve(CODEC_ID_MAX);
  codec_list.push_back(CODEC_ID_AAC);
  codec_list.push_back(CODEC_ID_AC3);
  codec_list.push_back(CODEC_ID_H262);
  codec_list.push_back(CODEC_ID_H263);
  codec_list.push_back(CODEC_ID_H264);
  codec_list.push_back(CODEC_ID_H265);

  if (codec_list != codec)
  {
    throw std::runtime_error("codec not found!");
  }

  if (codec_list == codec)
  {
    throw std::logic_error("codec has been found!");
  }
}
Valdemar_Rudolfovich
fonte
4
Eu não recomendaria abusar da sobrecarga do operador dessa maneira.
Leon
2
Leon, eu concordo com você, semanticamente não está correto. Eu o uso para tornar os testes de unidade mais claramente.
Valdemar_Rudolfovich
1
template <typename T> bool IsInVector(T what, std::vector<T> * vec)
{
    if(std::find(vec->begin(),vec->end(),what)!=vec->end())
        return true;
    return false;
}
user3157855
fonte
1

(C ++ 17 e superior):

pode usar std::searchtambém

Isso também é útil para pesquisar a sequência de elementos.

#include <algorithm>
#include <iostream>
#include <vector>

template <typename Container>
bool search_vector(const Container& vec, const Container& searchvec)
{
    return std::search(vec.begin(), vec.end(), searchvec.begin(), searchvec.end()) != vec.end();
}

int main()
{
     std::vector<int> v = {2,4,6,8};

     //THIS WORKS. SEARCHING ONLY ONE ELEMENT.
     std::vector<int> searchVector1 = {2};
     if(search_vector(v,searchVector1))
         std::cout<<"searchVector1 found"<<std::endl;
     else
         std::cout<<"searchVector1 not found"<<std::endl;

     //THIS WORKS, AS THE ELEMENTS ARE SEQUENTIAL.
     std::vector<int> searchVector2 = {6,8};
     if(search_vector(v,searchVector2))
         std::cout<<"searchVector2 found"<<std::endl;
     else
         std::cout<<"searchVector2 not found"<<std::endl;

     //THIS WILL NOT WORK, AS THE ELEMENTS ARE NOT SEQUENTIAL.
     std::vector<int> searchVector3 = {8,6};
     if(search_vector(v,searchVector3))
         std::cout<<"searchVector3 found"<<std::endl;
     else
         std::cout<<"searchVector3 not found"<<std::endl;
}

Também há flexibilidade de passar alguns algoritmos de busca. Consulte aqui.

https://en.cppreference.com/w/cpp/algorithm/search

Pavan Chandaka
fonte
1

Pessoalmente, usei modelos tarde para lidar com vários tipos de contêineres de uma só vez, em vez de lidar apenas com vetores. Encontrei um exemplo semelhante on-line (não me lembro de onde), então o crédito é para quem quer que tenha roubado isso. Esse padrão específico parece lidar com matrizes brutas também.

template <typename Container, typename T = typename std::decay<decltype(*std::begin(std::declval<Container>()))>::type>
bool contains(Container && c, T v)
{
    return std::find(std::begin(c), std::end(c), v) != std::end(c);
}
Pascal Laferrière
fonte
-4

Usando o Newton C ++ , é mais fácil, auto-documentado e mais rápido do que com o std :: find devido ao retorno de um bool diretamente.

bool exists_linear( INPUT_ITERATOR first, INPUT_ITERATOR last, const T& value )

bool exists_binary( INPUT_ITERATOR first, INPUT_ITERATOR last, const T& value )

Eu acho que é óbvio o que as funções fazem.

include <newton/algorithm/algorithm.hpp>

if ( newton::exists_linear(first, last, value) )
   do_this();
else
   do_that();
Moises Rojo
fonte