¿Cómo se verifica que un elemento esté en un conjunto?
¿Existe un equivalente más simple del siguiente código:
myset.find(x) != myset.end()
¿Cómo se verifica que un elemento esté en un conjunto?
¿Existe un equivalente más simple del siguiente código:
myset.find(x) != myset.end()
Respuestas:
La forma típica de comprobar la existencia en muchos contenedores STL, tales como std::map
, std::set
, ... es:
const bool is_in = container.find(element) != container.end();
std::find(container.begin(), container.end(), element) != container.end()
; O (N) el problema persiste, por supuesto ...
if(container.find(foo) == container.end())
debe hacer una búsqueda de árbol para encontrar el elemento; si no se encuentra, debe hacer una segunda búsqueda de árbol para encontrar la ubicación de inserción correcta. La variante original if(container.insert(foo).second) {...}
tiene el encanto de que solo necesita una sola búsqueda de árbol ...
set.contains(x)
que devuelve un bool en el estándar C ++ 20. No sé por qué nos ha llevado hasta 2020 introducir eso.
Otra forma de decir simplemente si existe un elemento es verificar el count()
if (myset.count(x)) {
// x is in the set, count is 1
} else {
// count zero, i.e. x not in the set
}
Sin embargo, la mayoría de las veces me encuentro necesitando acceso al elemento donde sea que verifique su existencia.
Así que tendría que encontrar el iterador de todos modos. Entonces, por supuesto, es mejor simplemente compararlo end
también.
set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
// do something with *it
}
C ++ 20
En C ++ 20, el conjunto obtiene una contains
función, por lo que lo siguiente se hace posible como se menciona en: https://stackoverflow.com/a/54197839/895245
if (myset.contains(x)) {
// x is in the set
} else {
// no x
}
count()
lugar de find()
nunca es mejor, pero potencialmente peor. Esto se debe a find()
que volverá después de la primera coincidencia, count()
siempre iterará sobre todos los elementos.
multiset
y multimap
pensé. Aún así es bueno señalar :)
set
contener un miembro que coincida, ¿no se implementaría la función de tal manera que se detenga después de localizar el primer elemento, en este caso, como señala Pieter? Respuesta útil en cualquier caso!
count()
nunca siendo más rápido que find()
) todavía se mantiene, la segunda parte no es aplicable a std::set
. Sin embargo, supongo que se puede hacer otro argumento a favor find()
: es más expresivo, es decir, enfatiza que estás tratando de encontrar un elemento en lugar de contar el número de ocurrencias.
Solo para aclarar, la razón por la que no hay ningún miembro como contains()
en estos tipos de contenedores es porque te abriría a escribir código ineficiente. Tal método probablemente solo lo haría this->find(key) != this->end()
internamente, pero considere lo que hace cuando la clave está realmente presente; en la mayoría de los casos, querrás obtener el elemento y hacer algo con él. Esto significa que tendría que hacer un segundo find()
, lo cual es ineficiente. Es mejor usar find directamente, para que pueda almacenar en caché su resultado, así:
auto it = myContainer.find(key);
if (it != myContainer.end())
{
// Do something with it, no more lookup needed.
}
else
{
// Key was not present.
}
Por supuesto, si no te importa la eficiencia, siempre puedes rodar la tuya, pero en ese caso probablemente no deberías estar usando C ++ ...;)
list::remove
, remove(makes_sense_only_for_vector, iterators)
...)
En C ++ 20 finalmente obtendremos el 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";
}
}
Si fuera a agregar una contains
función, podría verse así:
#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();
}
Esto funciona con std::set
otros contenedores STL e incluso con matrices de longitud fija:
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));
}
Como se señaló en los comentarios, involuntariamente utilicé una función nueva en C ++ 0x ( std::begin
y std::end
). Aquí está la implementación casi trivial de 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
y recuerda que solo es apropiado si lo único que necesitas saber es la existencia.
También puede verificar si un elemento está configurado o no mientras inserta el elemento. La versión de elemento único devuelve un par, con su par miembro :: primero configurado en un iterador que apunta al elemento recién insertado o al elemento equivalente que ya está en el conjunto. El par :: segundo elemento del par se establece en verdadero si se insertó un nuevo elemento o falso si ya existía un elemento equivalente.
Por ejemplo: supongamos que el conjunto ya tiene 20 como 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.
Si el elemento se inserta nuevamente, pair :: first apuntará a la posición del nuevo elemento en el conjunto.
Escribe lo tuyo:
template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
return container.find(elem) != container.end();
}
yo suelo
if(!my_set.count(that_element)) //Element is present...
;
Pero no es tan eficiente como
if(my_set.find(that_element)!=my_set.end()) ....;
Mi versión solo me ahorra tiempo al escribir el código. Lo prefiero de esta manera para la codificación competitiva.
count()
. Cualquiera que no pueda comprender que una función de retorno de enteros utilizada en una expresión booleana está probando un valor distinto de cero tendrá muchas, muchas otras tribulaciones en el gran mar de expresiones idiomáticas C / C ++. Y, como se señaló anteriormente, realmente debería ser tan eficiente para los conjuntos, que era la pregunta.
Pude escribir una contains
función general para std::list
y 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
Esto limpia un poco la sintaxis.
Pero no pude usar la magia de los parámetros de la plantilla de plantilla para hacer que esto funcione en contenedores STL arbitrarios.
// 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() ;
}
Cualquier comentario sobre cómo mejorar la última respuesta sería bueno.
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// Sintaxis general
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * en el siguiente código, estoy tratando de encontrar el elemento 4 en int set si está presente o no * /
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.
}