Як перевірити наявність елемента в наборі?
Чи є простіший еквівалент наступного коду:
myset.find(x) != myset.end()
Як перевірити наявність елемента в наборі?
Чи є простіший еквівалент наступного коду:
myset.find(x) != myset.end()
Відповіді:
Типовий спосіб перевірити існування в багатьох STL контейнерів , таких як std::map
, std::set
... це:
const bool is_in = container.find(element) != container.end();
std::find(container.begin(), container.end(), element) != container.end()
; Проблема O (N) залишається, звичайно ...
if(container.find(foo) == container.end())
потрібно зробити пошук дерева, щоб спочатку знайти елемент - якщо його не знайдено, то вам потрібно зробити пошук другого дерева, щоб знайти правильне місце вставки. Оригінальний варіант if(container.insert(foo).second) {...}
має привабливість, що йому потрібно лише одне пошукове дерево ...
set.contains(x)
те, що повертає bool у стандарті C ++ 20. Я не знаю, чому нам потрібно до 2020 року, щоб це зробити.
Ще один спосіб просто сказати, чи існує елемент, - це перевірити 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
}
count()
замість цього find()
ніколи не є кращим, але потенційно гіршим. Це тому find()
, що повернення після першого матчу count()
завжди буде повторювати всі елементи.
multiset
і multimap
я думав? Але все-таки добре зазначити :)
set
один член, який відповідає, чи не буде ця функція реалізована таким чином, щоб зупинитись після знаходження першого елемента, в цьому випадку, як вказує Пітер? Корисна відповідь у будь-якому випадку!
count()
ніколи не буває швидшою find()
) все-таки дотримується, друга частина дійсно не стосується std::set
. Однак, мабуть, можна зробити ще один аргумент на користь find()
: він більш виразний, тобто підкреслює, що ви намагаєтеся знайти елемент замість того, щоб рахувати кількість подій.
Просто для уточнення, причина, чому немає таких членів, як 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 ++ ...;)
list::remove
, remove(makes_sense_only_for_vector, iterators)
...)
У 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";
}
}
Якщо ви збираєтеся додати 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);
}
}
std::set
, і пам’ятайте, що вона підходить лише тоді, коли єдине, що вам потрібно знати, це існування.
Ви також можете перевірити, встановлений елемент чи ні, вставляючи елемент. Версія одноелементного повернення повертає пару, при цьому її пара-член :: спочатку встановлюється на ітератор, що вказує або на щойно вставлений елемент, або на еквівалентний елемент, який вже є в наборі. Пара: другий елемент у парі встановлено як 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.
Якщо елемент нещодавно вставлений, ніж пара :: перший, вкаже на положення нового елемента в наборі.
Напишіть своє:
template<class T>
bool checkElementIsInSet(const T& elem, const std::set<T>& container)
{
return container.find(elem) != container.end();
}
я використовую
if(!my_set.count(that_element)) //Element is present...
;
Але це не так ефективно, як
if(my_set.find(that_element)!=my_set.end()) ....;
Моя версія лише економить мій час на написанні коду. Я вважаю за краще цей спосіб для конкурентного кодування.
count()
. Кожен, хто не може зрозуміти, що функція, що повертається цілим числом, що використовується в булевому виразі, є тестуванням на ненульовий, матиме багато-багато інших поштовхів у великому морі ідіом C / C ++. І, як зазначалося вище, насправді це повинно бути настільки ж ефективним для наборів, в чому полягало питання.
Я зміг написати загальну 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();
// загальний синтаксис
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.
}