Como você verifica se um elemento está em um conjunto?
Existe um equivalente mais simples do seguinte código:
myset.find(x) != myset.end()
Como você verifica se um elemento está em um conjunto?
Existe um equivalente mais simples do seguinte código:
myset.find(x) != myset.end()
Respostas:
A forma habitual para verificar se há existência em muitos contêineres STL, como std::map
, std::set
... é:
const bool is_in = container.find(element) != container.end();
std::find(container.begin(), container.end(), element) != container.end()
; O (N) problema permanece, é claro ...
if(container.find(foo) == container.end())
necessário fazer uma pesquisa em árvore para encontrar o elemento primeiro - se não for encontrado, você precisará fazer uma segunda pesquisa em árvore para encontrar o local de inserção correto. A variante originais if(container.insert(foo).second) {...}
tem o charme que ele precisa de apenas uma única pesquisa árvore ...
set.contains(x)
que retorna um bool no padrão C ++ 20. Eu não sei por que ele nos levou até 2020 para conseguir que no.
Outra maneira de simplesmente dizer se existe um elemento é verificar o count()
if (myset.count(x)) {
// x is in the set, count is 1
} else {
// count zero, i.e. x not in the set
}
Na maioria das vezes, no entanto, me vejo precisando de acesso ao elemento sempre que verifico sua existência.
Então, eu teria que encontrar o iterador de qualquer maneira. Então, é claro, é melhor simplesmente compará-lo end
também.
set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
// do something with *it
}
C ++ 20
No conjunto C ++ 20, obtém uma contains
função, portanto, o seguinte se torna possível conforme mencionado em: https://stackoverflow.com/a/54197839/895245
if (myset.contains(x)) {
// x is in the set
} else {
// no x
}
count()
vez de find()
nunca é melhor, mas potencialmente pior. Isso ocorre porque find()
retornará após a primeira partida, count()
sempre iterará sobre todos os elementos.
multiset
e multimap
eu pensei? Ainda bom ressaltar embora :)
set
conter um membro que corresponda, a função não seria implementada de forma a parar após localizar o primeiro elemento, neste caso, como Pieter aponta? Resposta útil em qualquer caso!
count()
nunca sendo mais rápida que find()
) ainda seja válida, a segunda parte não é de fato aplicável a std::set
. No entanto, acho que outro argumento pode ser feito a favor de find()
: é mais expressivo, ou seja, enfatiza que você está tentando encontrar um elemento em vez de contar o número de ocorrências.
Apenas para esclarecer, a razão pela qual não há membros como contains()
esses tipos de contêineres é porque isso abriria você para escrever código ineficiente. Esse método provavelmente faria apenas this->find(key) != this->end()
internamente, mas considere o que você faz quando a chave está realmente presente; na maioria dos casos, você deseja obter o elemento e fazer algo com ele. Isso significa que você teria que fazer um segundo find()
, o que é ineficiente. É melhor usar o find diretamente, para que você possa armazenar em cache o resultado da seguinte maneira:
auto it = myContainer.find(key);
if (it != myContainer.end())
{
// Do something with it, no more lookup needed.
}
else
{
// Key was not present.
}
Obviamente, se você não se importa com eficiência, sempre pode fazer o seu próprio, mas nesse caso você provavelmente não deveria estar usando C ++ ...;)
list::remove
, remove(makes_sense_only_for_vector, iterators)
...)
No C ++ 20 , finalmente obteremos o std::set::contains
método.
#include <iostream>
#include <string>
#include <set>
int main()
{
std::set<std::string> example = {"Do", "not", "panic", "!!!"};
if(example.contains("panic")) {
std::cout << "Found\n";
} else {
std::cout << "Not found\n";
}
}
Se você fosse adicionar uma contains
função, ela poderia ser assim:
#include <algorithm>
#include <iterator>
template<class TInputIterator, class T> inline
bool contains(TInputIterator first, TInputIterator last, const T& value)
{
return std::find(first, last, value) != last;
}
template<class TContainer, class T> inline
bool contains(const TContainer& container, const T& value)
{
// This works with more containers but requires std::begin and std::end
// from C++0x, which you can get either:
// 1. By using a C++0x compiler or
// 2. Including the utility functions below.
return contains(std::begin(container), std::end(container), value);
// This works pre-C++0x (and without the utility functions below, but doesn't
// work for fixed-length arrays.
//return contains(container.begin(), container.end(), value);
}
template<class T> inline
bool contains(const std::set<T>& container, const T& value)
{
return container.find(value) != container.end();
}
Isso funciona com std::set
outros contêineres STL e até matrizes de comprimento fixo:
void test()
{
std::set<int> set;
set.insert(1);
set.insert(4);
assert(!contains(set, 3));
int set2[] = { 1, 2, 3 };
assert(contains(set2, 3));
}
Conforme apontado nos comentários, usei involuntariamente uma função nova para C ++ 0x ( std::begin
e std::end
). Aqui está a implementação quase trivial do VS2010:
namespace std {
template<class _Container> inline
typename _Container::iterator begin(_Container& _Cont)
{ // get beginning of sequence
return (_Cont.begin());
}
template<class _Container> inline
typename _Container::const_iterator begin(const _Container& _Cont)
{ // get beginning of sequence
return (_Cont.begin());
}
template<class _Container> inline
typename _Container::iterator end(_Container& _Cont)
{ // get end of sequence
return (_Cont.end());
}
template<class _Container> inline
typename _Container::const_iterator end(const _Container& _Cont)
{ // get end of sequence
return (_Cont.end());
}
template<class _Ty,
size_t _Size> inline
_Ty *begin(_Ty (&_Array)[_Size])
{ // get beginning of array
return (&_Array[0]);
}
template<class _Ty,
size_t _Size> inline
_Ty *end(_Ty (&_Array)[_Size])
{ // get end of array
return (&_Array[0] + _Size);
}
}
std::set
e lembre-se de que isso é apropriado apenas se a única coisa que você precisa saber é a existência.
Você também pode verificar se um elemento está definido ou não durante a inserção do elemento. A versão de elemento único retorna um par, com seu par de membros :: first definido para um iterador apontando para o elemento recém-inserido ou para o elemento equivalente já existente no conjunto. O elemento pair :: second no par é definido como true se um novo elemento foi inserido ou false se um elemento equivalente já existir.
Por exemplo: Suponha que o conjunto já tenha 20 como um elemento.
std::set<int> myset;
std::set<int>::iterator it;
std::pair<std::set<int>::iterator,bool> ret;
ret=myset.insert(20);
if(ret.second==false)
{
//do nothing
}
else
{
//do something
}
it=ret.first //points to element 20 already in set.
Se o elemento for inserido recentemente, o pair :: first apontará para a posição do novo elemento no conjunto.
Escreva o seu próprio:
template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
return container.find(elem) != container.end();
}
eu uso
if(!my_set.count(that_element)) //Element is present...
;
Mas não é tão eficiente quanto
if(my_set.find(that_element)!=my_set.end()) ....;
Minha versão economiza apenas meu tempo escrevendo o código. Eu prefiro assim para a codificação competitiva.
count()
. Qualquer pessoa que não consiga entender que uma função de retorno inteiro usada em uma expressão booleana está testando diferente de zero terá muitos, muitos outros problemas no grande mar de expressões em C / C ++. E, como observado acima, realmente deve ser tão eficiente para conjuntos, que era a questão.
Consegui escrever uma contains
função geral para std::list
e std::vector
,
template<typename T>
bool contains( const list<T>& container, const T& elt )
{
return find( container.begin(), container.end(), elt ) != container.end() ;
}
template<typename T>
bool contains( const vector<T>& container, const T& elt )
{
return find( container.begin(), container.end(), elt ) != container.end() ;
}
// use:
if( contains( yourList, itemInList ) ) // then do something
Isso limpa um pouco a sintaxe.
Mas eu não poderia usar a mágica de parâmetro de modelo de modelo para fazer com que isso funcionasse com contêineres stl arbitrários.
// NOT WORKING:
template<template<class> class STLContainer, class T>
bool contains( STLContainer<T> container, T elt )
{
return find( container.begin(), container.end(), elt ) != container.end() ;
}
Qualquer comentário sobre como melhorar a última resposta seria bom.
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// Sintaxe geral
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * no código abaixo, estou tentando encontrar o elemento 4 e o int definido se ele estiver presente ou não * /
set<int>::iterator ii = find(set1.begin(),set1.end(),4);
if(ii!=set1.end())
{
cout<<"element found";
set1.erase(ii);// in case you want to erase that element from set.
}