¿Cómo verificar que un elemento está en un conjunto std ::?

329

¿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()
fulmicoton
fuente
44
La única forma de ser más simple sería un predicado booleano: template <typename T> bool member (T const & item). Y eso se implementaría (debajo de las cubiertas) en términos de la línea sobre la que está preguntando.
Don Wakefield el

Respuestas:

399

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();
relajarse
fuente
25
Esto es específico para conjuntos y mapas. vectores, listas, etc. no tienen una función de miembro de búsqueda
wilhelmtell
8
OMI usando count () es mejor porque es simplemente más corto y se convierte en bool como se señala en la respuesta de Pieter. No entiendo por qué esta respuesta fue aceptada y tantos puntos ...
Огњен Шобајић
44
En aras de la exhaustividad: vectores / listas pueden usar std :: encuentran: std::find(container.begin(), container.end(), element) != container.end(); O (N) el problema persiste, por supuesto ...
Aconcagua
10
@MichaelMathews Con su variante: primero 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 ...
Aconcagua
23
hay un set.contains(x)que devuelve un bool en el estándar C ++ 20. No sé por qué nos ha llevado hasta 2020 introducir eso.
Gremwell
215

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 endtambié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 containsfunció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 
}
Pieter
fuente
102
Tenga en cuenta que usar en 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.
Frerich Raabe
34
@Frerich eso solo es relevante multisety multimappensé. Aún así es bueno señalar :)
Pieter
83
std :: set se implementa típicamente con una estructura de árbol ordenada, por lo que count () y find () tendrán O (logn). Ninguno de los dos iterará sobre todos los elementos del conjunto.
Alan
14
@FrerichRaabe - ¿Estás seguro? Dado que solo es posible setcontener 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!
Dan Nissenbaum
14
@DanNissenbaum Sí, tiene toda la razón (y también lo son + Peter y + Alan): para std :: set, las dos funciones son equivalentes en términos de rendimiento. Entonces, aunque la primera parte de mi comentario ( 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.
Frerich Raabe
42

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

Tim
fuente
44
¿Qué pasa con los conjuntos? Por lo general, ya tiene el elemento, pero solo quiere verificar si está dentro.
Elazar Leibovich
8
¿Tiene alguna referencia a si esta es la razón real por la que dicho método / función no está incluido en el stl, o es solo su conjetura?
Fabio A.
3
@FabioA. Es mi suposición educada.
Tim
1
@Adhemar, la consistencia no es exactamente el lado fuerte de STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich
3
No tiene sentido para mí no incluir una función porque alguien podría usarla incorrectamente si no supieran lo que estaban haciendo. La programación es para personas que pueden pensar por sí mismas y son responsables de su código y su rendimiento
Slawekwin
13

En C ++ 20 finalmente obtendremos el std::set::containsmé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";
    }
}
Denis Sablukov
fuente
6

Si fuera a agregar una containsfunció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::setotros 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));
}

Editar:

Como se señaló en los comentarios, involuntariamente utilicé una función nueva en C ++ 0x ( std::beginy 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);
    }

}
Sam Harwell
fuente
1
@Adhemar, en realidad fue ineficiente, pero no por la razón que mencionaste.
Sam Harwell el
@Paul: Asegúrate de incluir la especialización std::sety recuerda que solo es apropiado si lo único que necesitas saber es la existencia.
Sam Harwell el
@ 280Z28: std :: begin (container)? ¿Qué estándar STL es ese? No se compila en mi gcc.
stefaanv
@stefannv: je, es nuevo para C ++ 0x. Agregué la implementación de mi compilador anterior.
Sam Harwell el
2
@Adhemar: si sabe que el conjunto contiene un valor, entonces ya lo tiene. La única razón por la que necesitaría el iterador es para borrar el elemento del conjunto. Si todo lo que necesita es saber si una colección contiene o no un valor, entonces esta solución no es menos eficiente que cualquier otra solución.
Sam Harwell el
4

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.

Prashant Shubham
fuente
2

Escribe lo tuyo:

template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
  return container.find(elem) != container.end();
}
stefaanv
fuente
44
acabo de hacerlo: template <class T> static inline bool contiene (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
fulmicoton
44
@paul no crea funciones globales estáticas. coloque su función en un espacio de nombres anónimo: esa es la forma en C ++ de crear funciones que no se vincularán a otras unidades de compilación. Además, su parámetro T debe ser una referencia constante, para la corrección constante y para la eficiencia.
wilhelmtell
-1: Sin plantilla y en absoluto en estilo STL. Esto está bien si no está utilizando STL, pero si está utilizando STL, al menos debe intentar seguir sus estándares.
Sam Harwell el
1
@ 280Z28: Lamento que mi código no cumpla con sus estándares, solo estaba demostrando que si no le gusta la interfaz de STL, puede escribir la suya. Dios, no tentado? ¿Qué tan motivado tiene que ser? Su ejemplo se ve bien, eso no significa que el mío sea malo. Está más centrado en el set como lo solicitó el OP.
stefaanv
1
@ 280Z28: Estaba haciendo un punto. Pensé que las personas serían lo suficientemente inteligentes como para hacerse una idea.
stefaanv
2

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.

Manas Bondale
fuente
Sí, 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.
Ron Burk
0

Pude escribir una containsfunción general para std::listy 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.

bobobobo
fuente
Lo siento, no puedo escribir código de bloque en el comentario pero ¿qué pasa template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
fulmicoton
No funciona, porque std :: vector necesita un asignador adicional como argumento de plantilla y std :: set necesita un asignador y un argumento de plantilla menos. Estas líneas funcionarían: template <template <class, class> class STLContainer, class T, class A> bool contiene (STLContainer <T, A> container, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } template <template <class, class, class> class STLContainer, class T, class L, class A> bool contiene (STLContainer <T, A, L> container, T elt) {return find (container.begin (), container .end (), elt)! = container.end (); }
tgmath
0

// 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.
 }
sanjeev
fuente