Jak sprawdzić, czy element jest w zestawie?
Czy istnieje prostszy odpowiednik następującego kodu:
myset.find(x) != myset.end()
Jak sprawdzić, czy element jest w zestawie?
Czy istnieje prostszy odpowiednik następującego kodu:
myset.find(x) != myset.end()
Odpowiedzi:
Typowym sposobem sprawdzenia istnienia w wielu pojemników STL, takich jak std::map, std::set... jest:
const bool is_in = container.find(element) != container.end();
std::find(container.begin(), container.end(), element) != container.end(); Problem O (N) pozostaje, oczywiście ...
if(container.find(foo) == container.end())musisz najpierw wyszukać drzewo, aby najpierw znaleźć element - jeśli nie zostanie znaleziony, musisz zrobić drugie drzewo, aby znaleźć poprawną lokalizację wstawiania. Oryginalny wariant if(container.insert(foo).second) {...}ma urok, że potrzebuje tylko jednego wyszukiwania drzewa ...
set.contains(x)zwracający wartość bool w standardzie C ++ 20. Nie wiem, dlaczego zajęło nam to do 2020 roku.
Innym sposobem na stwierdzenie, czy element istnieje, jest sprawdzenie count()
if (myset.count(x)) {
// x is in the set, count is 1
} else {
// count zero, i.e. x not in the set
}
Najczęściej jednak potrzebuję dostępu do elementu wszędzie tam, gdzie sprawdzam jego istnienie.
W każdym razie musiałbym znaleźć iterator. Wtedy oczywiście lepiej po prostu to porównać end.
set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
// do something with *it
}
C ++ 20
W C ++ 20 zestaw dostaje containsfunkcję, więc możliwe staje się, jak wspomniano na: https://stackoverflow.com/a/54197839/895245
if (myset.contains(x)) {
// x is in the set
} else {
// no x
}
count()zamiast find()nigdy nie jest lepsze, ale potencjalnie gorsze. To dlatego, find()że powróci po pierwszym meczu, count()zawsze będzie iterować po wszystkich elementach.
multiseti multimappomyślałem? Wciąż warto jednak zwrócić uwagę :)
setjest zawarcie tylko jednego pasującego elementu, czy funkcja nie zostałaby zaimplementowana w taki sposób, aby zatrzymać po zlokalizowaniu pierwszego elementu, w tym przypadku, jak zauważa Pieter? Przydatna odpowiedź w każdym razie!
count()nigdy nie jest szybsza niż find()) nadal obowiązuje, druga część rzeczywiście nie ma zastosowania std::set. Wydaje mi się jednak, że można argumentować na korzyść find(): jest bardziej wyrazisty, tzn. Podkreśla, że próbujesz znaleźć element zamiast liczyć liczbę wystąpień.
Aby wyjaśnić, powodem, dla którego nie ma członka takiego jak contains()w tych typach kontenerów, jest to, że otworzyłoby cię to do pisania nieefektywnego kodu. Taka metoda prawdopodobnie po prostu wykona this->find(key) != this->end()wewnętrznie, ale zastanów się, co robisz, gdy klucz jest rzeczywiście obecny; w większości przypadków będziesz chciał zdobyć element i coś z nim zrobić. Oznacza to, że musisz zrobić sekundę find(), co jest nieefektywne. Lepiej jest użyć funkcji znajdź bezpośrednio, aby można było buforować swój wynik w następujący sposób:
auto it = myContainer.find(key);
if (it != myContainer.end())
{
// Do something with it, no more lookup needed.
}
else
{
// Key was not present.
}
Oczywiście, jeśli nie zależy Ci na wydajności, zawsze możesz rzucić własną, ale w takim przypadku prawdopodobnie nie powinieneś używać C ++ ...;)
list::remove, remove(makes_sense_only_for_vector, iterators)...)
W C ++ 20 w końcu otrzymamy std::set::containsmetodę.
#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";
}
}
Jeśli chcesz dodać containsfunkcję, może to wyglądać następująco:
#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();
}
Działa to z std::setinnymi kontenerami STL, a nawet tablicami o stałej długości:
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));
}
Jak wskazano w komentarzach, przypadkowo użyłem nowej funkcji w C ++ 0x ( std::begini std::end). Oto prawie trywialna implementacja 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::seti pamiętaj, że jest ona odpowiednia tylko wtedy, gdy jedyne , co musisz wiedzieć, to istnienie.
Możesz także sprawdzić, czy element jest w zestawie, czy nie podczas wstawiania elementu. Wersja z jednym elementem zwraca parę, a jej para elementów :: najpierw ustawiona na iterator wskazujący albo na nowo wstawiony element, albo na równoważny element już w zestawie. Para :: drugi element w tej parze jest ustawiony na true, jeśli wstawiono nowy element, lub false, jeśli element równoważny już istniał.
Na przykład: Załóżmy, że zestaw ma już 20 jako element.
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.
Jeśli element jest nowo wstawiony niż para :: najpierw wskaże pozycję nowego elementu w zestawie.
Napisz swoje własne:
template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
return container.find(elem) != container.end();
}
używam
if(!my_set.count(that_element)) //Element is present...
;
Ale to nie jest tak wydajne jak
if(my_set.find(that_element)!=my_set.end()) ....;
Moja wersja oszczędza tylko mój czas na pisanie kodu. Wolę to w ten sposób do konkurencyjnego kodowania.
count(). Każdy, kto nie jest w stanie pojąć, że funkcja zwracająca liczby całkowite używane w wyrażeniu logicznym testuje na wartość niezerową, będzie miał wiele, wiele innych problemów na wielkim morzu idiomów C / C ++. I, jak wspomniano powyżej, naprawdę powinno być tak samo wydajne dla zestawów, co było pytaniem.
Byłem w stanie napisać ogólną containsfunkcję dla std::listi 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
To nieco czyści składnię.
Ale nie mogłem użyć magii parametru szablonu szablonu, aby ta praca działała dowolnie.
// 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() ;
}
Wszelkie komentarze dotyczące poprawy ostatniej odpowiedzi byłyby miłe.
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// ogólna składnia
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * w poniższym kodzie próbuję znaleźć element 4 i int ustawić, jeśli jest obecny, czy nie * /
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.
}