Come si controlla che un elemento si trovi in un set?
Esiste un equivalente più semplice del seguente codice:
myset.find(x) != myset.end()
Come si controlla che un elemento si trovi in un set?
Esiste un equivalente più semplice del seguente codice:
myset.find(x) != myset.end()
Risposte:
Il modo tipico per verificare l'esistenza in molti contenitori STL, come std::map
, std::set
... è:
const bool is_in = container.find(element) != container.end();
std::find(container.begin(), container.end(), element) != container.end()
; Il problema O (N) rimane, ovviamente ...
if(container.find(foo) == container.end())
devi prima cercare un albero per trovare l'elemento - se non lo trovi, allora devi fare una seconda ricerca ad albero per trovare la posizione di inserimento corretta. La variante originale if(container.insert(foo).second) {...}
ha il fascino di aver bisogno di una sola ricerca di alberi ...
set.contains(x)
che restituisce un valore booleano nello standard C ++ 20. Non so perché ci sia
Un altro modo per dire semplicemente se esiste un elemento è controllare count()
if (myset.count(x)) {
// x is in the set, count is 1
} else {
// count zero, i.e. x not in the set
}
La maggior parte delle volte, tuttavia, mi ritrovo a dover accedere all'elemento ovunque controlli la sua esistenza.
Quindi dovrei trovare l'iteratore comunque. Quindi, ovviamente, è meglio confrontarlo semplicemente con quello end
.
set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
// do something with *it
}
C ++ 20
In C ++ 20 set ottiene una contains
funzione, quindi diventa possibile come indicato su: https://stackoverflow.com/a/54197839/895245
if (myset.contains(x)) {
// x is in the set
} else {
// no x
}
count()
posto di find()
non è mai migliore ma potenzialmente peggiore. Questo perché find()
tornerà dopo la prima corrispondenza, count()
ripeterà sempre su tutti gli elementi.
multiset
e multimap
ho pensato? Ancora buono da sottolineare però :)
set
contenere solo un membro corrispondente, la funzione non sarebbe implementata in modo tale da arrestarsi dopo aver localizzato il primo elemento, in questo caso, come sottolinea Pieter? Risposta utile in ogni caso!
count()
non essendo mai più veloce di find()
) è ancora valida, la seconda parte non è effettivamente applicabile std::set
. Tuttavia, immagino che si possa fare un altro argomento a favore find()
: è più espressivo, cioè sottolinea che stai cercando di trovare un elemento invece di contare il numero di occorrenze.
Giusto per chiarire, il motivo per cui non esiste un membro come contains()
in questi tipi di contenitori è perché ti aprirebbe alla scrittura di codice inefficiente. Un tale metodo probabilmente farebbe semplicemente un this->find(key) != this->end()
internamente, ma considera ciò che fai quando la chiave è effettivamente presente; nella maggior parte dei casi vorrai quindi ottenere l'elemento e fare qualcosa con esso. Ciò significa che dovresti fare un secondo find()
, il che è inefficiente. È meglio utilizzare trova direttamente, quindi puoi memorizzare nella cache i risultati, in questo modo:
auto it = myContainer.find(key);
if (it != myContainer.end())
{
// Do something with it, no more lookup needed.
}
else
{
// Key was not present.
}
Ovviamente, se non ti interessa l'efficienza, puoi sempre creare il tuo, ma in quel caso probabilmente non dovresti usare C ++ ...;)
list::remove
, remove(makes_sense_only_for_vector, iterators)
...)
In C ++ 20 finalmente avremo il std::set::contains
metodo.
#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 avessi intenzione di aggiungere una contains
funzione, potrebbe apparire così:
#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();
}
Funziona con std::set
altri contenitori STL e persino array a lunghezza fissa:
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));
}
Come sottolineato nei commenti, ho involontariamente usato una funzione nuova in C ++ 0x ( std::begin
e std::end
). Ecco l'implementazione quasi banale di 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 ricorda che è appropriato solo se l' unica cosa che devi sapere è l'esistenza.
È inoltre possibile verificare se un elemento è impostato o meno durante l'inserimento dell'elemento. La versione a singolo elemento restituisce una coppia, con la sua coppia membro :: prima impostata su un iteratore che punta all'elemento appena inserito o all'elemento equivalente già presente nel set. L'elemento pair :: second nella coppia è impostato su true se è stato inserito un nuovo elemento o false se esiste già un elemento equivalente.
Ad esempio: supponiamo che l'insieme abbia già 20 come 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 l'elemento è stato appena inserito rispetto a pair :: first punterà alla posizione del nuovo elemento nel set.
Scrivi il tuo:
template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
return container.find(elem) != container.end();
}
Io uso
if(!my_set.count(that_element)) //Element is present...
;
Ma non è così efficiente
if(my_set.find(that_element)!=my_set.end()) ....;
La mia versione mi fa solo risparmiare tempo nello scrivere il codice. Lo preferisco così per la codifica competitiva.
count()
. Chiunque non sia in grado di capire che una funzione di ritorno di numeri interi utilizzata in un'espressione booleana sta testando un valore diverso da zero avrà molti, molti altri travagli nel grande mare di idiomi C / C ++. E, come notato sopra, dovrebbe essere davvero efficiente per i set, che era la domanda.
Sono stato in grado di scrivere una contains
funzione generale per 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
Questo pulisce un po 'la sintassi.
Ma non ho potuto usare la magia dei parametri del modello di modello per rendere questo lavoro arbitrario contenitori stl.
// 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() ;
}
Qualsiasi commento sul miglioramento dell'ultima risposta sarebbe utile.
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// Sintassi generale
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * nel codice seguente sto cercando di trovare l'elemento 4 in e int impostato se è presente o meno * /
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.
}