Як дізнатись, чи елемент присутній у 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.