Як перевірити, чи є елемент у std :: set?


329

Як перевірити наявність елемента в наборі?

Чи є простіший еквівалент наступного коду:

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

4
Єдиним способом зробити простіше, ніж це, буде булевий предикат: template <typename T> bool member (T const & item). І це було б реалізовано (під обкладинками) з точки зору рядка, про який ви запитуєте.
Дон Уейкфілд

Відповіді:


399

Типовий спосіб перевірити існування в багатьох STL контейнерів , таких як std::map, std::set... це:

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

25
це специфічно для наборів та карт. вектори, списки тощо не мають функції пошуку члена.
wilhelmtell

8
ІМО, що використовує count (), краще, тому що він просто коротший і перетворюється на bool, як зазначено у відповіді Пітера. Я не розумію, чому цю відповідь прийняли і стільки балів ...
Огњен Шобајић

4
Заради повноти: вектори / списки можуть використовувати зЬй :: знайти: std::find(container.begin(), container.end(), element) != container.end(); Проблема O (N) залишається, звичайно ...
Аконкагуа

10
@MichaelMathews З вашим варіантом: if(container.find(foo) == container.end())потрібно зробити пошук дерева, щоб спочатку знайти елемент - якщо його не знайдено, то вам потрібно зробити пошук другого дерева, щоб знайти правильне місце вставки. Оригінальний варіант if(container.insert(foo).second) {...}має привабливість, що йому потрібно лише одне пошукове дерево ...
Aconcagua

23
є set.contains(x)те, що повертає bool у стандарті C ++ 20. Я не знаю, чому нам потрібно до 2020 року, щоб це зробити.
Гремвелл

215

Ще один спосіб просто сказати, чи існує елемент, - це перевірити count()

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

Однак у більшості випадків я відчуваю потребу в доступі до елемента, де б я не перевіряв його існування.

Тож мені все одно доведеться знайти ітератор. Тоді, звичайно, краще просто порівняти його endтеж.

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

C ++ 20

У наборі C ++ 20 отримує containsфункцію, тож стає можливим наступне, як згадується на сторінці : https://stackoverflow.com/a/54197839/895245

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

102
Зауважте, що використання count()замість цього find()ніколи не є кращим, але потенційно гіршим. Це тому find(), що повернення після першого матчу count()завжди буде повторювати всі елементи.
Фріріх Раабе

34
@Frerich це актуально лише для мене, multisetі multimapя думав? Але все-таки добре зазначити :)
Пітер

83
std :: set зазвичай реалізується з упорядкованою структурою дерева, тому count () та find () будуть мати O (logn). Також не буде повторюватися всі елементи в наборі.
Алан

14
@FrerichRaabe - Ви впевнені? Оскільки це може бути лише setодин член, який відповідає, чи не буде ця функція реалізована таким чином, щоб зупинитись після знаходження першого елемента, в цьому випадку, як вказує Пітер? Корисна відповідь у будь-якому випадку!
Дан Ніссенбаум

14
@DanNissenbaum Так, ви абсолютно праві (і так само + Пітер і + Алан): для std :: set дві функції еквівалентні за продуктивністю. Тож хоча перша частина мого коментаря ( count()ніколи не буває швидшою find()) все-таки дотримується, друга частина дійсно не стосується std::set. Однак, мабуть, можна зробити ще один аргумент на користь find(): він більш виразний, тобто підкреслює, що ви намагаєтеся знайти елемент замість того, щоб рахувати кількість подій.
Фріріх Раабе

42

Просто для уточнення, причина, чому немає таких членів, як contains()у цих типів контейнерів, полягає в тому, що це відкриє вам можливість написання неефективного коду. Такий метод, ймовірно, просто робиться this->find(key) != this->end()внутрішньо, але врахуйте, що ви робите, коли ключ дійсно присутній; у більшості випадків ви тоді захочете отримати елемент і зробити щось з ним. Це означає, що вам доведеться зайняти секунду find(), що неефективно. Краще скористатися функцією пошуку безпосередньо, щоб ви могли кешувати результат, наприклад:

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

Звичайно, якщо ви не піклуєтесь про ефективність, ви завжди можете скочувати свою власну, але в такому випадку ви, мабуть, не повинні використовувати C ++ ...;)


44
Що з наборами? Зазвичай у вас вже є елемент, але просто хочете перевірити, чи він у ньому.
Елазар Лейбович

8
Чи маєте ви будь-яке посилання на те, чи це фактична причина того, що такий метод / функція не включена в stl, чи це лише ваша здогадка?
Фабіо А.

3
@FabioA. Це моя здобута здогадка.
Тім

1
@Adhemar, послідовність не зовсім сильна сторона STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Елазар Лейбович

3
Мені немає сенсу не включати функцію, оскільки хтось може використовувати її неправильно, якби не знав, що робить. Програмування призначене для людей, які можуть думати про себе і відповідають за свій код та його ефективність
slawekwin

13

У C ++ 20 ми нарешті отримаємо std::set::containsметод.

#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

Якщо ви збираєтеся додати containsфункцію, це може виглядати приблизно так:

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

Це працює з std::setіншими контейнерами STL і навіть масивами фіксованої довжини:

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

Редагувати:

Як було зазначено в коментарях, я ненавмисно використав функцію, нову для C ++ 0x ( std::beginі std::end). Ось майже тривіальна реалізація від 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, насправді це було неефективно, але зовсім не з тієї причини, яку ви згадали.
Сем Харвелл

@Paul: Переконайтеся, що ви включили спеціалізацію std::set, і пам’ятайте, що вона підходить лише тоді, коли єдине, що вам потрібно знати, це існування.
Сем Харвелл

@ 280Z28: std :: begin (контейнер)? Який стандарт STL це? Він не компілюється на моєму gcc.
stefaanv

@stefannv: хе, це нове для C ++ 0x. Я додав виконання з мого компілятора вище.
Сем Харвелл

2
@Adhemar: Якщо ви знаєте, що набір містить значення, то ви вже це значення. Єдина причина, від якої вам знадобиться ітератор, - це стерти елемент із набору. Якщо все, що вам потрібно, це знати, містить чи не містить колекція значення, то це рішення є не менш ефективним, ніж будь-яке інше рішення.
Сем Харвелл

4

Ви також можете перевірити, встановлений елемент чи ні, вставляючи елемент. Версія одноелементного повернення повертає пару, при цьому її пара-член :: спочатку встановлюється на ітератор, що вказує або на щойно вставлений елемент, або на еквівалентний елемент, який вже є в наборі. Пара: другий елемент у парі встановлено як true, якщо новий елемент було вставлено або false, якщо еквівалентний елемент вже існував.

Наприклад: Припустимо, набір вже має 20 як елемент.

 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.

Якщо елемент нещодавно вставлений, ніж пара :: перший, вкаже на положення нового елемента в наборі.


2

Напишіть своє:

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

4
щойно зробив так: шаблон <клас T> статичний вбудований bool містить (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
фулмікотон

4
@paul не створює статичних глобальних функцій. замість цього розмістіть свою функцію в анонімному просторі імен: це C ++ спосіб створення функцій, які не зв’язуються з іншими підрозділами компіляції. Крім того, ваш T-параметр повинен бути еталоном const, для правильності const та ефективності.
wilhelmtell

-1: Чи не шаблонний і не зовсім в стилі STL. Це добре, якщо ви не використовуєте STL, але якщо ви використовуєте STL, вам слід хоча б спробувати дотримуватися його стандартів.
Сем Харвелл

1
@ 280Z28: Вибачте, що мій код не відповідає вашим стандартам, я просто показував, що якщо вам не подобається інтерфейс STL, ви можете написати свій власний. Боже, не шаблоновано? Наскільки шаблонно це має бути? Ваш приклад виглядає чудово, це не означає, що мій поганий. Він просто більше зосереджений на наборі, як просив ОП.
stefaanv

1
@ 280Z28: Я просто зазначав. Я думав, що люди будуть досить розумні, щоб отримати знімок.
stefaanv

2

я використовую

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

Але це не так ефективно, як

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

Моя версія лише економить мій час на написанні коду. Я вважаю за краще цей спосіб для конкурентного кодування.


Так, count(). Кожен, хто не може зрозуміти, що функція, що повертається цілим числом, що використовується в булевому виразі, є тестуванням на ненульовий, матиме багато-багато інших поштовхів у великому морі ідіом C / C ++. І, як зазначалося вище, насправді це повинно бути настільки ж ефективним для наборів, в чому полягало питання.
Рон Берк

0

Я зміг написати загальну containsфункцію для std::listі 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

Це трохи очищає синтаксис.

Але я не міг використовувати магію параметра шаблону шаблону, щоб зробити цю роботу довільними контейнерами stl.

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

Будь-які коментарі щодо покращення останньої відповіді були б непоганими.


Вибачте, я не можу написати блоковий код у коментарі, але що робити template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
fulmicoton

Це не працює, тому що std :: vector потрібен додатковий алокатор як аргумент шаблону, а std :: set потрібен алокатор та аргумент менш шаблону. Ці рядки працюють: шаблон <шаблон <клас, клас> клас STLContainer, клас T, клас A> bool містить (STLContainer <T, A> контейнер, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } шаблон <шаблон <клас, клас, клас> клас STLContainer, клас T, клас L, клас A> bool містить (STLContainer <T, A, L> контейнер, T elt) {return find (container.begin (), контейнер .end (), elt)! = контейнер.end (); }
tgmath

0

// загальний синтаксис

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

/ * у наведеному нижче коді я намагаюся знайти елемент 4 in та int, якщо він присутній чи ні * /

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.
 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.