Comment vérifiez-vous qu'un élément est dans un ensemble?
Existe-t-il un équivalent plus simple du code suivant:
myset.find(x) != myset.end()
Comment vérifiez-vous qu'un élément est dans un ensemble?
Existe-t-il un équivalent plus simple du code suivant:
myset.find(x) != myset.end()
Réponses:
La façon typique de vérifier l' existence dans de nombreux conteneurs STL tels que std::map
, std::set
... est la suivante :
const bool is_in = container.find(element) != container.end();
std::find(container.begin(), container.end(), element) != container.end()
; Le problème O (N) demeure, bien sûr ...
if(container.find(foo) == container.end())
doit faire une recherche d'arbre pour trouver l'élément en premier - s'il n'est pas trouvé, alors vous devez faire une deuxième recherche d'arbre pour trouver le bon emplacement d'insertion. La variante originale if(container.insert(foo).second) {...}
a le charme de ne nécessiter qu'une seule recherche d'arbre ...
set.contains(x)
qui renvoie un booléen dans la norme C ++ 20. Je ne sais pas pourquoi il nous a fallu jusqu'en 2020 pour que cela entre.
Une autre façon de simplement dire si un élément existe est de vérifier la count()
if (myset.count(x)) {
// x is in the set, count is 1
} else {
// count zero, i.e. x not in the set
}
La plupart du temps, cependant, je me retrouve à avoir besoin d'accéder à l'élément partout où je vérifie son existence.
Il faudrait donc que je trouve l'itérateur de toute façon. Alors, bien sûr, il vaut mieux simplement le comparer end
aussi.
set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
// do something with *it
}
C ++ 20
En C ++ 20, set obtient une contains
fonction, donc ce qui suit devient possible comme mentionné sur: https://stackoverflow.com/a/54197839/895245
if (myset.contains(x)) {
// x is in the set
} else {
// no x
}
count()
au lieu de find()
n'est jamais meilleur mais potentiellement pire. C'est parce find()
que reviendra après le premier match, count()
itérera toujours sur tous les éléments.
multiset
et multimap
je pensais? C'est quand même bon de le souligner :)
set
de contenir un membre qui correspond, la fonction ne serait-elle pas implémentée de manière à s'arrêter après avoir localisé le premier élément, dans ce cas, comme le souligne Pieter? Réponse utile dans tous les cas!
count()
n'est jamais plus rapide que find()
) tient toujours, la deuxième partie n'est en effet pas applicable à std::set
. Cependant, je suppose qu'un autre argument peut être avancé en faveur de find()
: il est plus expressif, c'est-à-dire qu'il souligne que vous essayez de trouver un élément au lieu de compter le nombre d'occurrences.
Juste pour clarifier, la raison pour laquelle il n'y a pas de membre comme contains()
dans ces types de conteneurs est parce que cela vous ouvrirait à l'écriture de code inefficace. Une telle méthode ne ferait probablement qu'une opération this->find(key) != this->end()
interne, mais réfléchissez à ce que vous faites lorsque la clé est effectivement présente; dans la plupart des cas, vous voudrez alors récupérer l'élément et en faire quelque chose. Cela signifie que vous devez en faire une seconde find()
, ce qui est inefficace. Il est préférable d'utiliser directement la recherche, afin de pouvoir mettre en cache votre résultat, comme ceci:
auto it = myContainer.find(key);
if (it != myContainer.end())
{
// Do something with it, no more lookup needed.
}
else
{
// Key was not present.
}
Bien sûr, si vous ne vous souciez pas de l'efficacité, vous pouvez toujours lancer le vôtre, mais dans ce cas, vous ne devriez probablement pas utiliser C ++ ...;)
list::remove
, remove(makes_sense_only_for_vector, iterators)
...)
En C ++ 20, nous aurons enfin la std::set::contains
méthode.
#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 vous deviez ajouter une contains
fonction, cela pourrait ressembler à ceci:
#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();
}
Cela fonctionne avec d' std::set
autres conteneurs STL et même des tableaux de longueur fixe:
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));
}
Comme indiqué dans les commentaires, j'ai involontairement utilisé une fonction nouvelle pour C ++ 0x ( std::begin
et std::end
). Voici l'implémentation quasi-triviale 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
, et rappelez-vous que ce n'est approprié que si la seule chose que vous devez savoir est l'existence.
Vous pouvez également vérifier si un élément est dans l'ensemble ou non lors de l'insertion de l'élément. La version à élément unique renvoie une paire, avec sa paire de membres :: first définie sur un itérateur pointant vers l'élément nouvellement inséré ou vers l'élément équivalent déjà dans l'ensemble. L'élément pair :: second de la paire est défini sur true si un nouvel élément a été inséré ou sur false si un élément équivalent existait déjà.
Par exemple: Supposons que l'ensemble a déjà 20 comme élément.
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 l'élément est nouvellement inséré, pair :: first pointera vers la position du nouvel élément dans set.
Écrivez votre propre:
template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
return container.find(elem) != container.end();
}
j'utilise
if(!my_set.count(that_element)) //Element is present...
;
Mais ce n'est pas aussi efficace que
if(my_set.find(that_element)!=my_set.end()) ....;
Ma version ne fait que gagner du temps en écrivant le code. Je le préfère de cette façon pour un codage compétitif.
count()
. Toute personne incapable de comprendre qu'une fonction de retour d'entier utilisée dans une expression booléenne teste une valeur non nulle va avoir de très nombreux autres échecs dans la grande mer des idiomes C / C ++. Et, comme indiqué ci-dessus, devrait vraiment être aussi efficace pour les ensembles, ce qui était la question.
J'ai pu écrire une contains
fonction générale pour std::list
et 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
Cela nettoie un peu la syntaxe.
Mais je ne pouvais pas utiliser la magie des paramètres de modèle de modèle pour faire fonctionner les conteneurs stl arbitraires.
// 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() ;
}
Tout commentaire sur l'amélioration de la dernière réponse serait bien.
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// Syntaxe générale
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * dans le code ci-dessous, j'essaie de trouver l'élément 4 dans et int défini s'il est présent ou non * /
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.
}