Як дізнатись, чи елемент присутній у std :: vector?


616

Все, що я хочу зробити, - це перевірити, чи існує елемент у векторі чи ні, тому я можу розібратися з кожним випадком.

if ( item_present )
   do_this();
else
   do_that();

2
пошук у векторі відбувається дуже повільно, оскільки вам потрібно дивитись на кожен елемент вектора, тому подумайте про використання карти, якщо ви робите багато
— оглядів

7
@naumcho: Якщо вектор сортується, завжди є двійковий пошук, як розміщено нижче. Це робить його таким же швидким, як карта, і якщо ви зберігаєте лише значення (а не ключові / значення карти), тоді він буде використовувати набагато менше пам'яті.
— Адам Хоуз

4
карти, безумовно, не найкращий вибір, але використання набору може бути корисним. Якщо вам потрібен час пошуку O (1), хеш_сет - це шлях.
— Філіп

Чудовий відповідь присутній на дублікаті питання: stackoverflow.com/a/3451045/472647
— CodeMouse92

1
Якщо ви збираєтеся шукати кілька разів для різних номерів, хеш-таблиця буде більш ефективною.
— NL628

Відповіді:


915

Ви можете використовувати std::findз <algorithm>:

#include <vector>
vector<int> vec; 
//can have other data types instead of int but must same datatype as item 
std::find(vec.begin(), vec.end(), item) != vec.end()

Це повертає бул ( trueякщо він присутній falseінакше). З вашим прикладом:

#include <algorithm>
#include <vector>

if ( std::find(vec.begin(), vec.end(), item) != vec.end() )
   do_this();
else
   do_that();

216
Я не бачу, як count () може бути швидшим, ніж find (), оскільки find () зупиняється, як тільки знайдеться один елемент, тоді як count () завжди повинен сканувати всю послідовність.
— Ерік Маленфант

114
Не забувайте, #include <algorithm>інакше у вас можуть виникнути дуже дивні помилки, такі як "не вдається знайти функцію узгодження в просторі імен std"
— rustyx

80
Хіба це нікого не турбувало, що, незважаючи на те, що STL є "об'єктно-орієнтованою", .find()вона все ще не є членом функції std::vector, як ви могли б сподіватися? Цікаво, чи це якимось наслідком шаблонування.
— bobobobo

71
@bobobobo: OOP не має нічого спільного з членами та не членами. І є широко поширена школа думки, що якщо щось не повинно бути членом, або коли це не дає жодної переваги, коли реалізується як член, то воно не повинно бути членом; std::vector<>::find()не давав би жодної переваги, ні вона не потрібна, тому ні, вона не повинна бути членом. Дивіться також en.wikipedia.org/wiki/Coupling_%28computer_programming%29
— Себастьян Мах

36
@phresnel Я б заперечував, що "коли це не дає жодної переваги, коли реалізується як член", для цієї справи є помилковим. Перевагою є спрощений і зрозуміліший інтерфейс. Наприклад: mvec.find(key) != mvec.cend()бажано std::find(mvec.cbegin(), mvec.cend(), key) != mvec.cend().
— swalog

113

Як говорили інші, використовуйте STL findабо find_ifфункції. Але якщо ви шукаєте в дуже великих векторів , і це впливає на продуктивність, ви можете сортувати вектор , а потім використовувати binary_search, lower_boundабо upper_boundалгоритми.


3
Хороша відповідь! Знайти завжди o (n). down_bound є o (log (n)), якщо використовується з ітераторами випадкового доступу.
— Стівен Едмондс

30
Сортування - це O (nlogn), тому воно варто лише в тому випадку, якщо ви робите більше, ніж пошук O (logn).
— liori

7
@liori Це правда, це залежить від моделей використання. Якщо вам потрібно сортувати його лише один раз, то багато разів здійснюйте багато пошуків, це може врятувати вас.
— Брайан Ніл

1
@Brian Neal, сортувати великий вектор варто, якщо на ньому має бути багато пошуків елементів. Сортування буде O (nlogn), а O (n) буде кращим, якщо потрібно знайти елемент лише один раз :)
— Swapnil B.

47

Скористайтеся знахідкою із заголовка алгоритму stl. Я проілюстрував його використання типу int. Ви можете використовувати будь-який тип, який вам подобається, доки ви можете порівняти рівність (перевантаження ==, якщо вам потрібно для вашого спеціального класу).

#include <algorithm>
#include <vector>

using namespace std;
int main()
{   
    typedef vector<int> IntContainer;
    typedef IntContainer::iterator IntIterator;

    IntContainer vw;

    //...

    // find 5
    IntIterator i = find(vw.begin(), vw.end(), 5);

    if (i != vw.end()) {
        // found it
    } else {
        // doesn't exist
    }

    return 0;
}

2
Залежно від потреб ОП, find_if () також може бути відповідним. Це дозволяє шукати за допомогою довільного предиката замість рівності.
— Ерік Маленфант

На жаль, побачили ваш коментар занадто пізно. Відповідь, яку я дав, також згадує find_if.
— Френк

39

Якщо ваш вектор не впорядкований, використовуйте запропонований MSN підхід:

if(std::find(vector.begin(), vector.end(), item)!=vector.end()){
      // Found the item
}

Якщо ваш вектор замовлений, використовуйте метод binary_search, який запропонував Брайан Ніл:

if(binary_search(vector.begin(), vector.end(), item)){
     // Found the item
}

двійковий пошук дає O (log n) найгірший показник, який є більш ефективним, ніж перший підхід. Для використання двійкового пошуку ви можете використовувати qsort, щоб спершу сортувати вектор, щоб гарантувати його впорядкування.


3
Ви не маєте на увазі std::sort? qsortдуже неефективний щодо векторів .... дивіться: stackoverflow.com/questions/12308243/…
— Jason R. Mick

1
Двійковий пошук буде більш ефективним для великих контейнерів, але для невеликих контейнерів простий лінійний пошук, швидше за все, буде таким же швидким або швидшим.
— BillT

21

Я використовую щось подібне ...

#include <algorithm>


template <typename T> 
const bool Contains( std::vector<T>& Vec, const T& Element ) 
{
    if (std::find(Vec.begin(), Vec.end(), Element) != Vec.end())
        return true;

    return false;
}

if (Contains(vector,item))
   blah
else
   blah

... тому що це насправді зрозуміло і читабельно. (Очевидно, ви можете повторно використовувати шаблон у кількох місцях).


і ви можете змусити його працювати для списків або векторів, використовуючи 2 назви імен
— Ерік Аронесті

@ErikAronesty ви можете піти з 1 аргументом шаблону, якщо використовуєте value_typeконтейнер для типу елемента. Я додав відповідь, як це.
— Мартін Бродхерст

13

В C ++ 11 ви можете використовувати any_of. Наприклад, якщо це vector<string> v;тоді:

if (any_of(v.begin(), v.end(), bind(equal_to<string>(), _1, item)))
   do_this();
else
   do_that();

Як варіант, використовуйте лямбда:

if (any_of(v.begin(), v.end(), [&](const std::string& elem) { return elem == item; }))
   do_this();
else
   do_that();

1
bind1stі bind2ndє застарілими , тому що C ++ 11 і повністю молодецький чи в C ++ 17. Використовуйте bindз placeholdersі / або лямбда замість цього.
— andreee

11

Ось функція, яка буде працювати для будь-якого контейнера:

template <class Container> 
const bool contains(const Container& container, const typename Container::value_type& element) 
{
    return std::find(container.begin(), container.end(), element) != container.end();
}

Зауважте, що ви можете піти з параметра 1 шаблону, оскільки ви можете витягнути його value_typeз контейнера. Вам потрібно typenameтому Container::value_type, що це залежне ім'я .


5
Зауважте, що це іноді трохи занадто широко - воно може працювати для std :: set, наприклад, але дає жахливу ефективність порівняно з функцією-членом find (). Я вважав, що найкраще додати спеціалізацію для контейнерів із швидшим пошуком (набір / карта, не упорядкований_ *)
— Andy Krouwel

10

Майте на увазі, що якщо ви збираєтеся робити багато пошукових запитів, є контейнери STL, які краще для цього. Я не знаю, що це за ваша програма, але асоціативні контейнери типу std :: map, можливо, варто розглянути.

std :: vector - це контейнер вибору, якщо у вас немає причини для іншого, і пошук за значенням може бути такою причиною.


Навіть при пошуку за значенням вектор може бути хорошим вибором, якщо він відсортований і ви використовуєте binary_search, нижній_bound або верхній_bound. Якщо вміст контейнера змінюється між переглядами, векторний не дуже хороший через необхідність сортування заново.
— Renze de Waal

8

Використовуйте функцію пошуку STL .

Майте на увазі, що є також функція find_if , яку ви можете використовувати, якщо ваш пошук є складнішим, тобто якщо ви не просто шукаєте елемент, але, наприклад, хочете подивитися, чи є елемент, який відповідає певній умова, наприклад, рядок, який починається з "abc". ( find_ifдав би вам ітератор, який вказує на перший такий елемент).


7

З прискоренням ви можете використовувати any_of_equal:

#include <boost/algorithm/cxx11/any_of.hpp>

bool item_present = boost::algorithm::any_of_equal(vector, element);

5

Ви можете спробувати цей код:

#include <algorithm>
#include <vector>

// You can use class, struct or primitive data type for Item
struct Item {
    //Some fields
};
typedef std::vector<Item> ItemVector;
typedef ItemVector::iterator ItemIterator;
//...
ItemVector vtItem;
//... (init data for vtItem)
Item itemToFind;
//...

ItemIterator itemItr;
itemItr = std::find(vtItem.begin(), vtItem.end(), itemToFind);
if (itemItr != vtItem.end()) {
    // Item found
    // doThis()
}
else {
    // Item not found
    // doThat()
}

3

Можна використовувати findфункцію, знайдену в stdпросторі імен, тобто std::find. Ви передаєте std::findфункцію beginі endітератор з вектора, який ви хочете шукати, разом з шуканим елементом і порівнюєте отриманий ітератор до кінця вектора, щоб побачити, чи відповідають вони чи ні.

std::find(vector.begin(), vector.end(), item) != vector.end()

Ви також можете знизити цей ітератор і використовувати його як звичайний, як і будь-який інший ітератор.


3

Ви також можете використовувати лічильник. Він поверне кількість елементів, присутніх у векторі.

int t=count(vec.begin(),vec.end(),item);

11
findшвидше count, тому що він не продовжує рахувати після першого матчу.
— Camille Goudeseune

2

Якщо ви хочете знайти рядок у векторі:

    struct isEqual
{
    isEqual(const std::string& s): m_s(s)
    {}

    bool operator()(OIDV* l)
    {
        return l->oid == m_s;
    }

    std::string m_s;
};
struct OIDV
{
    string oid;
//else
};
VecOidv::iterator itFind=find_if(vecOidv.begin(),vecOidv.end(),isEqual(szTmp));

2

Ще один зразок із використанням операторів C ++.

#include <vector>
#include <algorithm>
#include <stdexcept>

template<typename T>
inline static bool operator ==(const std::vector<T>& v, const T& elem)
{
  return (std::find(v.begin(), v.end(), elem) != v.end());
}

template<typename T>
inline static bool operator !=(const std::vector<T>& v, const T& elem)
{
  return (std::find(v.begin(), v.end(), elem) == v.end());
}

enum CODEC_ID {
  CODEC_ID_AAC,
  CODEC_ID_AC3,
  CODEC_ID_H262,
  CODEC_ID_H263,
  CODEC_ID_H264,
  CODEC_ID_H265,
  CODEC_ID_MAX
};

void main()
{
  CODEC_ID codec = CODEC_ID_H264;
  std::vector<CODEC_ID> codec_list;

  codec_list.reserve(CODEC_ID_MAX);
  codec_list.push_back(CODEC_ID_AAC);
  codec_list.push_back(CODEC_ID_AC3);
  codec_list.push_back(CODEC_ID_H262);
  codec_list.push_back(CODEC_ID_H263);
  codec_list.push_back(CODEC_ID_H264);
  codec_list.push_back(CODEC_ID_H265);

  if (codec_list != codec)
  {
    throw std::runtime_error("codec not found!");
  }

  if (codec_list == codec)
  {
    throw std::logic_error("codec has been found!");
  }
}

4
Я б не рекомендував таким чином зловживати перевантаженням оператора.
— Леон

2
Леон, я згоден з тобою, семантично це неправильно. Я використовую його для більш чіткого аналізу тестів.
— Вальдемар_Рудольфович

1
template <typename T> bool IsInVector(T what, std::vector<T> * vec)
{
    if(std::find(vec->begin(),vec->end(),what)!=vec->end())
        return true;
    return false;
}

1

(C ++ 17 і вище):

може std::searchтакож використовувати

Це також корисно для пошуку послідовності елементів.

#include <algorithm>
#include <iostream>
#include <vector>

template <typename Container>
bool search_vector(const Container& vec, const Container& searchvec)
{
    return std::search(vec.begin(), vec.end(), searchvec.begin(), searchvec.end()) != vec.end();
}

int main()
{
     std::vector<int> v = {2,4,6,8};

     //THIS WORKS. SEARCHING ONLY ONE ELEMENT.
     std::vector<int> searchVector1 = {2};
     if(search_vector(v,searchVector1))
         std::cout<<"searchVector1 found"<<std::endl;
     else
         std::cout<<"searchVector1 not found"<<std::endl;

     //THIS WORKS, AS THE ELEMENTS ARE SEQUENTIAL.
     std::vector<int> searchVector2 = {6,8};
     if(search_vector(v,searchVector2))
         std::cout<<"searchVector2 found"<<std::endl;
     else
         std::cout<<"searchVector2 not found"<<std::endl;

     //THIS WILL NOT WORK, AS THE ELEMENTS ARE NOT SEQUENTIAL.
     std::vector<int> searchVector3 = {8,6};
     if(search_vector(v,searchVector3))
         std::cout<<"searchVector3 found"<<std::endl;
     else
         std::cout<<"searchVector3 not found"<<std::endl;
}

Також є гнучкість проходження деяких алгоритмів пошуку. Зверніться сюди.

https://en.cppreference.com/w/cpp/algorithm/search


1

Я особисто використовував шаблони пізнього часу для обробки декількох типів контейнерів одночасно, а не для роботи лише з векторами. Я знайшов подібний приклад в Інтернеті (не пам'ятаю, куди), тому кредит належить тому, з ким я це пограбував. Цей конкретний малюнок, здається, також обробляє необроблені масиви.

template <typename Container, typename T = typename std::decay<decltype(*std::begin(std::declval<Container>()))>::type>
bool contains(Container && c, T v)
{
    return std::find(std::begin(c), std::end(c), v) != std::end(c);
}

-4

Використовувати Newton C ++ простіше, документується самостійно та швидше, ніж за допомогою std :: find через повернення bool безпосередньо.

bool exists_linear( INPUT_ITERATOR first, INPUT_ITERATOR last, const T& value )

bool exists_binary( INPUT_ITERATOR first, INPUT_ITERATOR last, const T& value )

Я думаю, що очевидно, що виконують функції.

include <newton/algorithm/algorithm.hpp>

if ( newton::exists_linear(first, last, value) )
   do_this();
else
   do_that();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.