Bagaimana cara memeriksa apakah suatu elemen ada di std :: set?

329

Bagaimana Anda memeriksa bahwa suatu elemen ada di dalam suatu set?

Apakah ada persamaan yang lebih sederhana dari kode berikut:

myset.find(x) != myset.end()
fulmicoton
sumber
4
Satu-satunya cara untuk mendapatkan yang lebih sederhana dari itu adalah predikat boolean: template <typename T> anggota bool (T const & item). Dan itu akan diterapkan (di bawah sampul) dalam hal garis yang Anda tanyakan.
Don Wakefield

Jawaban:

399

Cara khas untuk memeriksa keberadaan di banyak kontainer STL seperti std::map, std::set, ... adalah:

const bool is_in = container.find(element) != container.end();
beristirahat
sumber
25
ini khusus untuk set dan peta. vektor, daftar dll. tidak memiliki fungsi temukan anggota.
wilhelmtell
8
IMO menggunakan count () lebih baik karena hanya lebih pendek dan dikonversi menjadi bool seperti dicatat dalam jawaban oleh Pieter. Saya tidak mengerti mengapa jawaban ini diterima dan begitu banyak poin ...
Огњен Шобајић
4
Demi kelengkapan: vektor / daftar dapat menggunakan std :: find std::find(container.begin(), container.end(), element) != container.end():; O (N) masalah tetap, tentu saja ...
Aconcagua
10
@MichaelMathews Dengan varian Anda: if(container.find(foo) == container.end())perlu melakukan pencarian pohon untuk menemukan elemen pertama - jika tidak ditemukan, maka Anda perlu melakukan pencarian pohon kedua untuk menemukan lokasi penyisipan yang benar. Varian asli if(container.insert(foo).second) {...}memiliki pesona yang dibutuhkan hanya satu pencarian pohon tunggal ...
Aconcagua
23
ada set.contains(x)yang mengembalikan bool dalam standar C ++ 20. Saya tidak tahu mengapa kami sampai tahun 2020 untuk mendapatkannya.
gremwell
215

Cara lain untuk mengatakan apakah suatu elemen ada adalah dengan memeriksa count()

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

Namun, sebagian besar waktu, saya menemukan diri saya membutuhkan akses ke elemen di mana pun saya memeriksa keberadaannya.

Jadi saya harus menemukan iteratornya. Maka, tentu saja, lebih baik membandingkannya endjuga.

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

C ++ 20

Dalam set C ++ 20 mendapat containsfungsi, jadi yang berikut menjadi mungkin seperti yang disebutkan di: https://stackoverflow.com/a/54197839/895245

if (myset.contains(x)) {
  // x is in the set
} else {
  // no x 
}
Pieter
sumber
102
Perhatikan bahwa menggunakan count()bukannya find()tidak pernah lebih baik tetapi berpotensi lebih buruk. Ini karena find()akan kembali setelah pertandingan pertama, count()akan selalu beralih ke semua elemen.
Frerich Raabe
34
@ Frich itu hanya relevan untuk multisetdan multimapsaya pikir? Masih bagus untuk menunjukkan :)
Pieter
83
std :: set biasanya diimplementasikan dengan struktur pohon yang diurut, jadi hitung () dan find () keduanya akan memiliki O (logn). Tidak akan mengulangi semua elemen di set.
Alan
14
@FrerichRaabe - Apakah Anda yakin? Karena hanya mungkin setmengandung satu anggota yang cocok, bukankah fungsi akan diimplementasikan sedemikian rupa untuk berhenti setelah menemukan elemen pertama, dalam hal ini, seperti yang ditunjukkan Pieter? Bagaimanapun, jawaban yang berguna!
Dan Nissenbaum
14
@DanNissenbaum Ya, Anda benar (dan begitu juga + Peter dan + Alan): untuk std :: set, kedua fungsi tersebut setara dalam hal kinerja. Jadi meskipun bagian pertama dari komentar saya ( count()tidak pernah lebih cepat dari find()) masih berlaku, bagian kedua memang tidak berlaku std::set. Namun, saya kira argumen lain dapat dibuat dalam mendukung find(): itu lebih ekspresif, yaitu menekankan bahwa Anda mencoba untuk menemukan elemen daripada menghitung jumlah kemunculan.
Frerich Raabe
42

Hanya untuk memperjelas, alasan mengapa tidak ada anggota seperti contains()dalam jenis wadah ini adalah karena itu akan membuka Anda untuk menulis kode yang tidak efisien. Metode seperti itu mungkin hanya akan melakukan this->find(key) != this->end()internal, tetapi pertimbangkan apa yang Anda lakukan ketika kuncinya memang ada; dalam kebanyakan kasus, Anda akan ingin mendapatkan elemen dan melakukan sesuatu dengannya. Ini berarti Anda harus melakukan yang kedua find(), yang tidak efisien. Lebih baik menggunakan find secara langsung, sehingga Anda dapat men-cache hasil Anda, seperti:

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

Tentu saja, jika Anda tidak peduli dengan efisiensi, Anda selalu dapat menggunakan roll Anda sendiri, tetapi dalam hal ini Anda mungkin tidak boleh menggunakan C ++ ...;)

Tim
sumber
44
Bagaimana dengan set? Biasanya Anda sudah memiliki elemen, tetapi hanya ingin memeriksa apakah sudah masuk.
Elazar Leibovich
8
Apakah Anda memiliki referensi apakah ini alasan sebenarnya metode / fungsi tersebut tidak termasuk dalam stl, atau hanya dugaan Anda yang berpendidikan?
Fabio A.
3
@FabioA. Ini tebakan saya yang terpelajar.
Tim
1
@Adhemar, konsistensi bukanlah sisi kuat dari STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich
3
Tidak masuk akal bagi saya untuk tidak memasukkan fitur karena seseorang mungkin menggunakannya secara salah jika mereka tidak tahu apa yang mereka lakukan. Pemrograman adalah untuk orang-orang yang dapat berpikir untuk diri mereka sendiri dan bertanggung jawab atas kode mereka dan kinerjanya
slawekwin
13

Dalam C ++ 20 kita akhirnya akan mendapatkan std::set::containsmetode.

#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";
    }
}
Denis Sablukov
sumber
6

Jika Anda akan menambahkan containsfungsi, mungkin terlihat seperti ini:

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

Ini bekerja dengan std::set, wadah STL lainnya, dan bahkan susunan dengan panjang tetap:

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

Edit:

Seperti yang ditunjukkan dalam komentar, saya tidak sengaja menggunakan fungsi baru untuk C ++ 0x ( std::begindan std::end). Berikut ini implementasi hampir sepele dari 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);
    }

}
Sam Harwell
sumber
1
@ Adhemar, itu sebenarnya tidak efisien, tetapi tidak sama sekali karena alasan yang Anda sebutkan.
Sam Harwell
@ Paul: Pastikan Anda memasukkan spesialisasi std::set, dan ingat bahwa itu hanya sesuai jika satu - satunya hal yang perlu Anda ketahui adalah keberadaan.
Sam Harwell
@ 280Z28: std :: begin (wadah)? Apa standar STL itu? Itu tidak dikompilasi di gcc saya.
stefaanv
@stefannv: heh, ini baru untuk C ++ 0x. Saya menambahkan implementasi dari kompiler saya di atas.
Sam Harwell
2
@ Adhemar: Jika Anda tahu set berisi nilai, maka Anda sudah nilainya. Satu-satunya alasan Anda membutuhkan iterator adalah untuk menghapus elemen dari set. Jika semua yang Anda butuhkan adalah untuk mengetahui apakah kumpulan berisi nilai atau tidak, maka solusi ini tidak kalah efisien daripada solusi lainnya.
Sam Harwell
4

Anda juga dapat memeriksa apakah suatu elemen diatur atau tidak saat memasukkan elemen. Versi elemen tunggal mengembalikan pasangan, dengan pasangan anggotanya :: set pertama ke iterator yang menunjuk ke elemen yang baru saja dimasukkan atau ke elemen setara yang sudah ada di set. Elemen pasangan :: kedua dalam pasangan diatur ke true jika elemen baru dimasukkan atau salah jika elemen yang setara sudah ada.

Sebagai contoh: Misalkan set sudah memiliki 20 sebagai elemen.

 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.

Jika elemen baru dimasukkan daripada pasangan :: pertama akan menunjuk ke posisi elemen baru di set.

Prashant Shubham
sumber
2

Tulis milik Anda sendiri:

template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
  return container.find(elem) != container.end();
}
stefaanv
sumber
4
baru saja melakukannya: template <class T> static inline bool berisi (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
fulmicoton
4
@ paul jangan membuat fungsi global statis. sebaliknya, tempatkan fungsi Anda di namespace anonim: itulah cara C ++ untuk membuat fungsi yang tidak akan ditautkan ke unit kompilasi lainnya. juga, parameter T Anda harus menjadi referensi const, untuk kebenaran const dan untuk efisiensi.
wilhelmtell
-1: Tidak templated dan sama sekali tidak dalam gaya STL. Ini bagus jika Anda tidak menggunakan STL, tetapi jika Anda menggunakan STL Anda setidaknya harus berusaha untuk mengikuti standarnya.
Sam Harwell
1
@ 280Z28: Maaf kode saya tidak sesuai dengan standar Anda, saya hanya menunjukkan bahwa jika Anda tidak menyukai antarmuka STL, Anda dapat menulis sendiri. Astaga, tidak templated? Bagaimana harus templated? Teladan Anda terlihat baik-baik saja, bukan berarti milik saya buruk. Itu hanya lebih fokus pada set seperti yang diminta oleh OP.
stefaanv
1
@ 280Z28: Saya baru saja menegaskan maksudnya. Saya pikir orang akan cukup pintar untuk mendapatkan gambar.
stefaanv
2

saya menggunakan

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

Tetapi itu tidak seefisien

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

Versi saya hanya menghemat waktu saya dalam menulis kode. Saya lebih suka cara ini untuk pengkodean kompetitif.

Manas Bondale
sumber
Ya, count(). Siapa pun yang tidak dapat memahami bahwa fungsi integer-return yang digunakan dalam ekspresi Boolean sedang menguji untuk non-nol akan memiliki banyak, banyak kerja keras lain di lautan besar idiom C / C ++. Dan, seperti disebutkan di atas, benar-benar harus seefisien untuk set, yang merupakan pertanyaan.
Ron Burk
0

Saya dapat menulis containsfungsi umum untuk std::listdan 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

Ini sedikit membersihkan sintaks.

Tapi saya tidak bisa menggunakan templat parameter templat ajaib untuk membuat karya ini wadah stl sewenang-wenang.

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

Setiap komentar tentang meningkatkan jawaban terakhir akan menyenangkan.

bobobobo
sumber
Maaf saya tidak bisa menulis kode blokir dalam komentar, tetapi bagaimana template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
fulmicoton
Ini tidak berfungsi, karena std :: vector memerlukan pengalokasi tambahan sebagai argumen template dan std :: set membutuhkan pengalokasi dan argumen template yang lebih sedikit. Baris-baris ini akan berfungsi: templat <templat <class, class> class STLContainer, class T, class A> bool berisi (STLContainer <T, A> container, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } templat <templat <class, class, class> class STLContainer, class T, class L, class A> bool berisi (STLContainer <T, A, L> container, T elt) {return find (container.begin (), container .end (), elt)! = container.end (); }
tgmath
0

// Sintaksis umum

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

/ * dalam kode di bawah ini saya mencoba untuk menemukan elemen 4 di dan int ditetapkan apakah ada atau tidak * /

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.
 }
sanjeev
sumber