Bir elemanın bir std :: setinde olup olmadığını nasıl kontrol edebilirim?


329

Bir öğenin kümede olup olmadığını nasıl kontrol edersiniz?

Aşağıdaki kodun daha basit bir eşdeğeri var mı:

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

4
Bundan daha basit olmanın tek yolu bir boole yüklemidir: şablon <typename T> bool üyesi (T const & item). Ve bu, sorduğunuz satır açısından (kapakların altında) uygulanacaktır.
Don Wakefield

Yanıtlar:


399

Tipik bir yol gibi birçok STL kaplarda varlığını denetlemek için std::map, std::set... geçerli:

const bool is_in = container.find(element) != container.end();

25
bu kümeler ve haritalar için özeldir. vektörler, listeler vs. bir üye bulma fonksiyonuna sahip değildir.
wilhelmtell

8
Count () kullanarak IMO daha iyidir, çünkü sadece daha kısadır ve Pieter'in cevabında belirtildiği gibi boole dönüşür. Bu cevabın neden kabul edildiğini ve çok puan
aldığını anlamıyorum

4
Tamlık uğruna: vektörler / listeler std :: find std::find(container.begin(), container.end(), element) != container.end():; O (N) problemi elbette var ...
Aconcagua

10
@MichaelMathews Varyantınızla: if(container.find(foo) == container.end())önce öğeyi bulmak için bir ağaç araması yapmanız gerekir - bulunmazsa, doğru ekleme konumunu bulmak için ikinci bir ağaç araması yapmanız gerekir. Orijinal varyant if(container.insert(foo).second) {...}, sadece bir tek ağaç aramasına ihtiyaç duyduğu cazibeye sahiptir ...
Aconcagua

23
Bir orada set.contains(x)bu döner C ++ 20 standardında Boole. Bunu almak için neden 2020'ye kadar götürdüğümü bilmiyorum.
gremwell

215

Bir öğenin var olup olmadığını söylemenin başka bir yolu, count()

if (myset.count(x)) {
   // x is in the set, count is 1
} else {
   // count zero, i.e. x not in the set
}

Ancak çoğu zaman kendimi varlığını kontrol ettiğim her yerde öğeye erişmeye ihtiyaç duyuyorum.

Bu yüzden yineleyiciyi bulmak zorundayım. Sonra, elbette, bunu da basitçe karşılaştırmak daha iyidir end.

set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
   // do something with *it
}

C ++ 20

C ++ 20 setinde bir containsfonksiyon elde edilir, bu nedenle aşağıdaki gibi mümkün olur: https://stackoverflow.com/a/54197839/895245

if (myset.contains(x)) {
  // x is in the set
} else {
  // no x 
}

102
count()Bunun yerine kullanmanın find()asla daha iyi değil, potansiyel olarak daha kötü olduğunu unutmayın. Bunun nedeni find(), ilk maçtan sonra geri dönecek, count()her zaman tüm unsurları tekrarlayacaktır.
Frerich Raabe

34
@Frerich sadece alakalı multisetve multimapdüşündüm? Yine de işaret etmek iyi :)
Pieter

83
std :: set genellikle sıralı bir ağaç yapısıyla uygulanır, bu nedenle count () ve find () öğelerinin her ikisinde de O (logn) olur. Kümedeki tüm öğeler üzerinde de yineleme yapılmaz.
Alan

14
@FrerichRaabe - Emin misiniz? Yalnızca seteşleşen bir üye içermesi mümkün olduğundan, işlev ilk öğeyi bulduktan sonra, bu durumda Pieter'in belirttiği gibi durdurulacak şekilde uygulanamaz mı? Her durumda faydalı cevap!
Dan Nissenbaum

14
@DanNissenbaum Evet, kesinlikle haklısınız (+ Peter ve + Alan da öyle): std :: set için, iki işlev performans açısından eşdeğerdir. Dolayısıyla, yorumumun ilk kısmı ( count()asla daha hızlı olmamakla birlikte find()) hala geçerli olsa da, ikinci kısmı gerçekten geçerli değildir std::set. Bununla birlikte, sanırım başka bir argüman lehine yapılabilir find(): daha etkileyici, yani olay sayısını saymak yerine bir öğe bulmaya çalıştığınızı vurgular.
Frerich Raabe

42

Açıklığa kavuşturmak gerekirse contains(), bu kapsayıcı türlerinde olduğu gibi üye bulunmamasının nedeni, verimsiz kod yazmaya açmanızdır. Böyle bir yöntem muhtemelen sadece this->find(key) != this->end()dahili olarak yapardı , ancak anahtar gerçekten mevcut olduğunda ne yaptığınızı düşünün; çoğu durumda öğeyi almak ve onunla bir şeyler yapmak istersiniz. Bu, bir saniye yapmanız gerektiği anlamına gelir find(), bu da verimsizdir. Doğrudan bulmayı kullanmak daha iyidir, böylece sonucunuzu önbelleğe alabilirsiniz, böylece:

auto it = myContainer.find(key);
if (it != myContainer.end())
{
    // Do something with it, no more lookup needed.
}
else
{
    // Key was not present.
}

Tabii ki, verimliliği umursamıyorsanız, her zaman kendi başınıza dönebilirsiniz, ancak bu durumda muhtemelen C ++ kullanmamalısınız ...;)


44
Setler ne olacak? Genellikle elemana zaten sahipsiniz, ama sadece
içeri

8
Bunun böyle bir yöntemin / fonksiyonun stl'ye dahil edilmemesinin gerçek nedeni olup olmadığı hakkında herhangi bir referansınız var mı, yoksa sadece eğitimli tahmininiz mi?
Fabio A.

3
@FabioA. Benim eğitimli tahminim.
Tim

1
@Adhemar, tutarlılık tam olarak STL'nin güçlü tarafı değil ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich

3
Bir özelliği dahil etmemek benim için bir anlam ifade etmiyor, çünkü birileri ne yaptığını bilmiyorsa yanlış kullanabilir. Programlama, kendileri düşünebilen ve kodlarından ve performansından sorumlu olan insanlar içindir
slawekwin

13

In 20 C ++ nihayet alırsınız std::set::containsyöntemi.

#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

Bir containsişlev ekleyecekseniz , şöyle görünebilir:

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

Bu std::set, diğer STL kapları ve hatta sabit uzunluklu dizilerle çalışır:

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

Düzenle:

Yorumlarda belirtildiği gibi, yanlışlıkla C ++ 0x ( std::beginve std::end) için yeni bir işlev kullandım . İşte VS2010'dan neredeyse önemsiz bir uygulama:

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, aslında oldu ama Bahsettiğiniz nedenle hiç verimsiz.
Sam Harwell

@ Paul: Uzmanlığını eklediğinizden emin olun std::setve bunun yalnızca bilmeniz gereken tek şey varoluşsa uygun olduğunu unutmayın .
Sam Harwell

@ 280Z28: std :: başlangıç ​​(konteyner)? Hangi STL standardı bu? Benim gcc üzerinde derlemiyor.
stefaanv

@ stefannv: he, C ++ 0x için yeni. Yukarıdaki derleyiciden uygulamayı ekledim.
Sam Harwell

2
@Adhemar: Kümenin bir değer içerdiğini biliyorsanız, zaten değeri vardır. Yineleyiciye ihtiyacınızın tek nedeni, öğeyi kümeden silmektir. Eğer tüm ihtiyacınız bir koleksiyon değeri içerip içermediği bilmektir, o zaman bu çözüm daha az verimli diğer çözümlerden daha olduğunu.
Sam Harwell

4

Ayrıca, öğeyi eklerken bir öğenin ayarlanıp ayarlanmadığını da kontrol edebilirsiniz. Tek elemanlı sürüm, üye çifti :: ilk olarak yeni eklenen öğeye veya zaten kümede bulunan eşdeğer öğeye işaret eden bir yineleyiciye ayarlanmış bir çift döndürür. Yeni bir öğe eklenmişse çiftteki pair :: second öğesi true değerine veya eşdeğer bir öğe zaten varsa false değerine ayarlanır.

Örneğin: Setin zaten öğe olarak 20'si olduğunu varsayalım.

 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.

Eleman pair :: 'den daha yeni eklenirse, ilk önce kümedeki yeni elemanın konumunu gösterecektir.


2

Kendin yaz:

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

4
bunu yaptı: şablon <sınıf T> statik satır içi bool içerir (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
fulmicoton

4
@paul statik global işlevler oluşturmaz. işlevinizi anonim bir ad alanına koyun: diğer derleme birimlerine bağlanmayacak işlevler yaratmanın C ++ yolu budur. ayrıca, T parametrenizin sabit doğruluk ve verimlilik için bir sabit referans olması gerekir.
wilhelmtell

-1: şablonu Değil değil hiç STL tarzında. STL kullanmıyorsanız bu sorun olmaz, ancak STL kullanıyorsanız en azından standartlarına uymaya çalışmalısınız.
Sam Harwell

1
@ 280Z28: Kodumun standartlarınıza uygun olmadığı için üzgünüm, sadece STL'nin arayüzünü beğenmezseniz kendi kodunuzu yazabileceğinizi gösteriyordum. Tanrım, şablon değil mi? Ne kadar şablon olmalı? Örneğin iyi görünüyor, bu benimkinin kötü olduğu anlamına gelmiyor. Sadece OP tarafından sorulduğu gibi sete daha fazla odaklanıyor.
stefaanv

1
@ 280Z28: Sadece bir şey ifade ediyordum. İnsanların resmi alacak kadar zeki olduklarını düşündüm.
stefaanv

2

kullanırım

if(!my_set.count(that_element)) //Element is present...
;

Ama bu kadar etkili değil

if(my_set.find(that_element)!=my_set.end()) ....;

Sürümüm yalnızca kod yazarken zaman kazanıyor. Rekabetçi kodlama için bu şekilde tercih ederim.


Evet count(),. Boole ifadesinde kullanılan bir tamsayı döndürme işlevinin sıfır olmayanları sınadığını kavrayamayan herkes, C / C ++ deyimlerinin büyük denizinde çok sayıda başka travmaya sahip olacaktır. Ve, yukarıda belirtildiği gibi, gerçekten setler için etkili olmalı, ki soru buydu.
Ron Burk

0

İçin genel bir containsişlev yazabildim std::listve 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

Bu sözdizimini biraz temizler.

Ama bu şablon keyfi stl kapları yapmak için şablon şablonu parametre büyü kullanamadı .

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

Son cevabı geliştirmeyle ilgili herhangi bir yorum iyi olurdu.


Üzgünüm gerçekten yorumda blok kodu yazamıyorum ama ne hakkında template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
fulmicoton

Çalışmıyor, çünkü std :: vector şablon argümanı olarak ek bir ayırıcıya ihtiyaç duyuyor ve std :: set bir ayırıcıya ve daha az şablon argümanına ihtiyaç duyuyor. Bu satırlar işe yarar: şablon <şablon <sınıf, sınıf> sınıf STLContainer, sınıf T, sınıf A> bool (STLContainer <T, A> kapsayıcı, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } şablon <şablon <sınıf, sınıf, sınıf> sınıf STLContainer, sınıf T, sınıf L, sınıf A> bool içerir (STLContainer <T, A, L> konteyner, T elt) {return find (container.begin (), konteyner .end (), elt)! = container.end (); }
tgmath

0

// genel Sözdizimi

       set<int>::iterator ii = find(set1.begin(),set1.end(),"element to be searched");

/ * aşağıdaki kodda varsa ve değilse 4 set ve int set eleman bulmaya çalışıyorum * /

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.
 }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.