Jak sprawdzić, czy element jest w std :: set?


329

Jak sprawdzić, czy element jest w zestawie?

Czy istnieje prostszy odpowiednik następującego kodu:

myset.find(x) != myset.end()

4
Jedynym sposobem na uproszczenie byłoby predykat boolowski: szablon <typ nazwy T> element bool (T const i element). I to byłoby zaimplementowane (pod przykryciem) w odniesieniu do linii, o którą pytasz.
Don Wakefield,

Odpowiedzi:


399

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();

25
jest to specyficzne dla zestawów i map. wektory, listy itp. nie mają funkcji znajdowania członka.
wilhelmtell,

8
IMO korzystające z count () jest lepsze, ponieważ jest po prostu krótsze i konwertuje na bool, jak zauważono w odpowiedzi Pietera. Nie rozumiem, dlaczego ta odpowiedź została zaakceptowana i tyle punktów ...
Огњен Шобајић

4
Dla kompletności wywodu: wektorach / listy można użyć std :: znaleźć: std::find(container.begin(), container.end(), element) != container.end(); Problem O (N) pozostaje, oczywiście ...
Aconcagua,

10
@MichaelMathews W Twoim wariancie: 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 ...
Aconcagua,

23
istnieje standard set.contains(x)zwracający wartość bool w standardzie C ++ 20. Nie wiem, dlaczego zajęło nam to do 2020 roku.
Gremwell

215

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 
}

102
Pamiętaj, że używanie 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.
Frerich Raabe

34
@Frerich, który dotyczy tylko multiseti multimappomyślałem? Wciąż warto jednak zwrócić uwagę :)
Pieter,

83
std :: set jest zwykle implementowany z uporządkowaną strukturą drzewa, więc count () i find () będą miały O (logn). Ani też nie będzie iterował wszystkich elementów zestawu.
Alan

14
@FrerichRaabe - Czy jesteś pewien? Ponieważ możliwe 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!
Dan Nissenbaum

14
@ DanNissenbaum Tak, masz dokładnie rację (podobnie jak + Peter i + Alan): dla std :: set te dwie funkcje są równoważne pod względem wydajności. Tak więc, mimo że pierwsza część mojego komentarza ( 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ń.
Frerich Raabe

42

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


44
Co z zestawami? Zwykle masz już element, ale po prostu chcesz sprawdzić, czy jest w
środku

8
Czy masz jakieś odniesienia do tego, czy jest to rzeczywisty powód, dla którego taka metoda / funkcja nie jest uwzględniona w STL, czy jest to tylko twoje wykształcone przypuszczenie?
Fabio A.

3
@FabioA. To moje wykształcone przypuszczenie.
Tim

1
@Adhemar, spójność nie jest dokładnie mocną stroną STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich

3
Nie ma dla mnie sensu nie włączać funkcji, ponieważ ktoś mógłby użyć jej niepoprawnie, gdyby nie wiedział, co robi. Programowanie jest przeznaczone dla osób, które mogą myśleć same za siebie i są odpowiedzialne za swój kod i jego działanie
slawekwin

13

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";
    }
}

6

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));
}

Edytować:

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);
    }

}

1
@Adhemar, to faktycznie było nieefektywne, ale wcale nie z powodu, o którym wspomniałeś.
Sam Harwell,

@Paul: Upewnij się, że podałeś specjalizację std::seti pamiętaj, że jest ona odpowiednia tylko wtedy, gdy jedyne , co musisz wiedzieć, to istnienie.
Sam Harwell,

@ 280Z28: std :: begin (container)? Co to jest standard STL? Nie kompiluje się na moim gcc.
stefaanv

@stefannv: heh, jest nowy dla C ++ 0x. Dodałem implementację z mojego kompilatora powyżej.
Sam Harwell,

2
@Adhemar: Jeśli wiesz, że zestaw zawiera wartość, to już jesteś tą wartością. Jedynym powodem, dla którego potrzebujesz iteratora, jest usunięcie elementu ze zbioru. Jeśli wszystko, czego potrzebujesz, to wiedzieć, czy dana kolekcja zawiera wartość, to rozwiązanie jest nie mniej wydajne niż jakiekolwiek inne rozwiązanie.
Sam Harwell,

4

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.


2

Napisz swoje własne:

template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
  return container.find(elem) != container.end();
}

4
właśnie to zrobiłem: szablon <klasa T> statyczny wbudowany bool zawiera (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
fulmicoton,

4
@paul nie tworzy statycznych funkcji globalnych. zamiast tego umieść swoją funkcję w anonimowej przestrzeni nazw: to sposób tworzenia funkcji, które nie będą łączyły się z innymi jednostkami kompilacji. również twój parametr T powinien być stałym odniesieniem, dla stałej poprawności i wydajności.
wilhelmtell

-1: Nie matrycy, a nie w ogóle w stylu STL. Jest to w porządku, jeśli nie używasz STL, ale jeśli używasz STL, powinieneś przynajmniej spróbować przestrzegać jego standardów.
Sam Harwell,

1
@ 280Z28: Przykro mi, że mój kod nie odpowiada twoim standardom, po prostu pokazałem, że jeśli nie podoba ci się interfejs STL, możesz napisać własny. Jezu, nie na szablonie? Jak musi być szablonem? Twój przykład wygląda dobrze, to nie znaczy, że mój jest zły. Jest po prostu bardziej skoncentrowany na planie, o co poprosił PO.
stefaanv

1
@ 280Z28: Właśnie miałem rację. Myślałem, że ludzie będą wystarczająco inteligentni, aby uzyskać zdjęcie.
stefaanv

2

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.


Tak 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.
Ron Burk,

0

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.


Przykro mi, ale nie mogę tak naprawdę napisać kodu blokowego w komentarzu, ale co z template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
fulmicoton

To nie działa, ponieważ std :: vector wymaga dodatkowego alokatora jako argumentu szablonu, a std :: set potrzebuje alokatora i mniej argumentu szablonu. Te wiersze działają: szablon <szablon <klasa, klasa> klasa STLContainer, klasa T, klasa A> bool zawiera (STLContainer <T, A> kontener, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } szablon <szablon <klasa, klasa, klasa> klasa STLContainer, klasa T, klasa L, klasa A> bool zawiera (STLContainer <kontener T, A, L>, T elt) {return find (container.begin (), kontener .end (), elt)! = container.end (); }
tgmath

0

// 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.
 }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.