Wie überprüfen Sie, ob sich ein Element in einer Menge befindet?
Gibt es ein einfacheres Äquivalent zum folgenden Code:
myset.find(x) != myset.end()
Wie überprüfen Sie, ob sich ein Element in einer Menge befindet?
Gibt es ein einfacheres Äquivalent zum folgenden Code:
myset.find(x) != myset.end()
Antworten:
Der typische Weg für Existenz in vielen STL - Containern zu überprüfen , wie std::map
, std::set
... ist:
const bool is_in = container.find(element) != container.end();
std::find(container.begin(), container.end(), element) != container.end()
; O (N) Problem bleibt natürlich ...
if(container.find(foo) == container.end())
muss zuerst eine Baumsuche durchgeführt werden, um das Element zu finden. Wenn es nicht gefunden wird, müssen Sie eine zweite Baumsuche durchführen, um die richtige Einfügeposition zu finden. Die ursprüngliche Variante if(container.insert(foo).second) {...}
hat den Reiz, dass sie nur eine einzige Baumsuche benötigt ...
set.contains(x)
, der im C ++ 20-Standard einen Bool zurückgibt. Ich weiß nicht, warum wir bis 2020 gebraucht haben, um das zu erreichen.
Eine andere Möglichkeit, einfach festzustellen, ob ein Element vorhanden ist, besteht darin, das zu überprüfen count()
if (myset.count(x)) {
// x is in the set, count is 1
} else {
// count zero, i.e. x not in the set
}
Meistens benötige ich jedoch Zugriff auf das Element, wo immer ich es überprüfe.
Also müsste ich sowieso den Iterator finden. Dann ist es natürlich besser, es einfach zu vergleichen end
.
set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
// do something with *it
}
C ++ 20
In C ++ 20 erhält set eine contains
Funktion, sodass Folgendes möglich wird: https://stackoverflow.com/a/54197839/895245
if (myset.contains(x)) {
// x is in the set
} else {
// no x
}
count()
anstelle von find()
niemals besser, aber möglicherweise schlechter ist. Dies liegt daran find()
, dass nach der ersten Übereinstimmung count()
immer alle Elemente durchlaufen werden.
multiset
und multimap
ich dachte? Immer noch gut darauf hinzuweisen :)
set
, ein übereinstimmendes Mitglied zu enthalten, würde die Funktion in diesem Fall nicht so implementiert, dass sie nach dem Auffinden des ersten Elements stoppt, wie Pieter betont? Nützliche Antwort auf jeden Fall!
count()
niemals schneller ist als find()
) immer noch gültig ist, gilt der zweite Teil in der Tat nicht für std::set
. Ich denke jedoch, dass ein anderes Argument dafür find()
angeführt werden kann : Es ist aussagekräftiger, dh es wird betont, dass Sie versuchen, ein Element zu finden, anstatt die Anzahl der Vorkommen zu zählen.
Um dies zu verdeutlichen, liegt der Grund dafür, dass es contains()
in diesen Containertypen kein Mitglied wie dieses gibt, darin, dass Sie dadurch ineffizienten Code schreiben können. Eine solche Methode würde wahrscheinlich nur this->find(key) != this->end()
intern durchgeführt, aber überlegen Sie, was Sie tun, wenn der Schlüssel tatsächlich vorhanden ist. In den meisten Fällen möchten Sie dann das Element abrufen und etwas damit tun. Dies bedeutet, dass Sie eine Sekunde ausführen müssen find()
, was ineffizient ist. Es ist besser, find direkt zu verwenden, damit Sie Ihr Ergebnis wie folgt zwischenspeichern können:
auto it = myContainer.find(key);
if (it != myContainer.end())
{
// Do something with it, no more lookup needed.
}
else
{
// Key was not present.
}
Wenn Sie sich nicht für Effizienz interessieren, können Sie natürlich immer Ihre eigenen rollen, aber in diesem Fall sollten Sie wahrscheinlich nicht C ++ verwenden ...;)
list::remove
, remove(makes_sense_only_for_vector, iterators)
...)
In C ++ 20 bekommen wir endlich die std::set::contains
Methode.
#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";
}
}
Wenn Sie eine contains
Funktion hinzufügen möchten, sieht diese möglicherweise folgendermaßen aus:
#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();
}
Dies funktioniert mit std::set
anderen STL-Containern und sogar Arrays mit fester Länge:
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));
}
Wie in den Kommentaren erwähnt, habe ich ungewollt eine neue Funktion in C ++ 0x ( std::begin
und std::end
) verwendet. Hier ist die nahezu triviale Implementierung von 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
, und denken Sie daran, dass dies nur dann angemessen ist, wenn das einzige, was Sie wissen müssen, die Existenz ist.
Sie können beim Einfügen des Elements auch überprüfen, ob ein Element festgelegt ist oder nicht. Die Einzelelementversion gibt ein Paar zurück, wobei das Element pair :: first auf einen Iterator gesetzt ist, der entweder auf das neu eingefügte Element oder auf das entsprechende Element zeigt, das bereits in der Menge enthalten ist. Das pair :: second-Element im Paar wird auf true gesetzt, wenn ein neues Element eingefügt wurde, oder auf false, wenn bereits ein gleichwertiges Element vorhanden war.
Zum Beispiel: Angenommen, die Menge hat bereits 20 als 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.
Wenn das Element neu eingefügt wird, zeigt pair :: first auf die Position des neuen Elements in set.
Schreibe dein Eigenes:
template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
return container.find(elem) != container.end();
}
ich benutze
if(!my_set.count(that_element)) //Element is present...
;
Aber es ist nicht so effizient wie
if(my_set.find(that_element)!=my_set.end()) ....;
Meine Version spart nur Zeit beim Schreiben des Codes. Ich bevorzuge es auf diese Weise für wettbewerbsfähige Codierung.
count()
. Jeder, der nicht verstehen kann, dass eine in einem Booleschen Ausdruck verwendete Ganzzahl-Rückgabefunktion auf Nicht-Null prüft, wird im großen Meer der C / C ++ - Redewendungen viele, viele andere Probleme haben. Und wie oben erwähnt, sollte es für Sets wirklich genauso effizient sein, was die Frage war.
Ich konnte eine allgemeine contains
Funktion für std::list
und schreiben 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
Dies bereinigt die Syntax ein wenig.
Aber ich konnte nicht Template Template Parameter Magic verwenden , um diese Arbeit beliebige STL-Container zu machen.
// 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() ;
}
Alle Kommentare zur Verbesserung der letzten Antwort wären nett.
template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
// allgemeine Syntax
set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");
/ * im folgenden Code versuche ich, Element 4 in und int set zu finden, ob es vorhanden ist oder nicht * /
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.
}