【问题标题】:How to find out if an item is present in a std::vector?如何找出一个项目是否存在于 std::vector 中?
【发布时间】:2010-10-08 22:55:17
【问题描述】:

我要做的就是检查一个元素是否存在于向量中,这样我就可以处理每种情况。

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

【问题讨论】:

  • 在向量中搜索非常慢,因为您必须查看向量的每个元素,因此如果您要进行大量查找,请考虑使用地图
  • @naumcho:如果对向量进行排序,则始终存在二进制搜索,如下所示。这使它与映射一样快,如果您只存储值(而不是键/值映射),那么它将使用更少的内存。
  • 地图当然不是最佳选择,但使用 set 可能会有用。如果你需要 O(1) 的查找时间,hash_set 是最好的选择。
  • 重复问题的绝佳答案:stackoverflow.com/a/3451045/472647
  • 如果要多次搜索不同的数字,哈希表会更有效。

标签: c++ vector std


【解决方案1】:

你可以从<algorithm>使用std::find

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

【讨论】:

  • 我看不出 count() 怎么能比 find() 快,因为 find() 会在找到一个元素后立即停止,而 count() 总是必须扫描整个序列。
  • 别忘了#include &lt;algorithm&gt;,否则你可能会遇到非常奇怪的错误,比如'can't find matching function in namespace std'
  • 尽管 STL 是“面向对象的”,但.find() 仍然不是std::vector 的成员函数,正如你所料它应该是?我想知道这是否是模板化的结果。
  • @bobobobo:OOP 与成员与非成员无关。并且有一个广泛的思想流派,如果某些东西不必成为成员,或者当它作为成员实施时没有任何优势时,那么它不应该成为成员; std::vector&lt;&gt;::find() 不会提供任何优势,也不需要它,因此,不,它不应该是会员。另见en.wikipedia.org/wiki/Coupling_%28computer_programming%29
  • @phresnel 我认为“当它作为成员实施时没有任何优势时”对于这种情况是错误的。优点是界面简洁明了。例如:mvec.find(key) != mvec.cend() 优于 std::find(mvec.cbegin(), mvec.cend(), key) != mvec.cend()
【解决方案2】:

正如其他人所说,使用 STL findfind_if 函数。但是,如果您在非常大的向量中搜索并且这会影响性能,您可能需要对向量进行排序,然后使用binary_searchlower_boundupper_bound 算法。

【讨论】:

  • 好答案!查找总是 o(n)。如果与随机访问迭代器一起使用,lower_bound 为 o(log(n))。
  • 虽然排序是 O(nlogn),所以只有当你做的搜索超过 O(logn) 时才值得。
  • @liori 是的,这取决于您的使用模式。如果你只需要排序一次,那么重复多次搜索它可以节省你的时间。
  • @Brian Neal,如果必须在其上进行许多元素搜索,那么对大向量进行排序是值得的。排序将是 O(nlogn),如果一个元素只能找到一次,O(n) 会更好:)
  • 请注意这可能会对您的分支预测造成严重破坏。
【解决方案3】:

使用 stl 的算法头中的 find。我已经说明了它与 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;
}

【讨论】:

  • 根据 OP 的需要, find_if() 也可能是合适的。它允许使用任意谓词而不是相等进行搜索。
  • 糟糕,看到您的评论太晚了。我给出的答案也提到了 find_if。
【解决方案4】:

如果您的向量没有排序,请使用 MSN 建议的方法:

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

如果您的向量是有序的,请使用 Brian Neal 建议的 binary_search 方法:

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

二分搜索产生 O(log n) 最坏情况的性能,这比第一种方法更有效。为了使用二分查找,可以先用qsort对向量进行排序,保证它是有序的。

【讨论】:

  • 你不是说std::sort吗? qsort 在向量上的效率非常低.... 见:stackoverflow.com/questions/12308243/…
  • 对于较大的容器,二分搜索的性能会更好,但对于小容器,简单的线性搜索可能会一样快,甚至更快。
【解决方案5】:

我用这样的东西...

#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 如果您使用容器中的 value_type 作为元素类型,则可以使用 1 个模板参数。我已经添加了这样的答案。
  • 你基本上是在写:if true return true else return false。该方法可以是一个:return std::find(Vec.begin(), Vec.end(), Element) != Vec.end();
【解决方案6】:

在 C++11 中,您可以使用 any_of。例如如果是vector&lt;string&gt; v; 那么:

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

或者,使用 lambda:

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

【讨论】:

  • bind1stbind2nddeprecated since C++11 并在 C++17 中完全删除。将 bindplaceholders 和/或 lambda 一起使用。
【解决方案7】:

这是一个适用于任何容器的函数:

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_typedependent name

【讨论】:

  • 请注意,这有时有点过于宽泛——例如,它适用于 std::set,但与 find() 成员函数相比,性能很差。我发现最好为具有更快搜索的容器添加专业化(set/map,unordered_*)
  • 也许有一天他们最终会将它添加到标准库中......而不是人们不得不一遍又一遍地问如何重新发明这样一个小轮子。现在完全可行的是,在 C++20 中我们有 ranges,因此可以将其称为 Range 而不是 Container,并且 Bob 是你的叔叔。
【解决方案8】:

请记住,如果您要进行大量查找,那么 STL 容器会更适合您。我不知道您的应用程序是什么,但像 std::map 这样的关联容器可能值得考虑。

std::vector 是首选容器,除非您有另一个原因,而按值查找可能是这样的原因。

【讨论】:

  • 即使使用按值查找,向量也是一个不错的选择,只要它已排序并且您使用 binary_search、lower_bound 或 upper_bound。如果容器的内容在查找之间发生变化,则vector不是很好,因为需要再次排序。
【解决方案9】:

使用 STL find 函数。

请记住,还有一个find_if 函数,如果您的搜索更复杂,您可以使用它,即如果您不只是在寻找一个元素,而是,例如,想看看是否有满足特定条件的元素,例如以“abc”开头的字符串。 (find_if 会给你一个指向第一个这样的元素的迭代器)。

【讨论】:

    【解决方案10】:

    使用 boost 你可以使用any_of_equal:

    #include <boost/algorithm/cxx11/any_of.hpp>
    
    bool item_present = boost::algorithm::any_of_equal(vector, element);
    

    【讨论】:

      【解决方案11】:

      你可以试试这个代码:

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

      【讨论】:

        【解决方案12】:

        您可以使用find 函数,该函数位于std 命名空间中,即std::find。您将要搜索的向量中的std::find 函数传递给beginend 迭代器,以及您要查找的元素,并将生成的迭代器与向量的末尾进行比较以查看它们是否匹配或不是。

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

        您还可以取消引用该迭代器并正常使用它,就像任何其他迭代器一样。

        【讨论】:

          【解决方案13】:

          您也可以使用计数。 它将返回向量中存在的项目数。

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

          【讨论】:

          • findcount 快,因为它不会在第一场比赛后继续计数。
          【解决方案14】:
          template <typename T> bool IsInVector(const T & what, const std::vector<T> & vec)
          {
              return std::find(vec.begin(),vec.end(),what)!=vec.end();
          }
          

          【讨论】:

          • 你为什么要冒着风险让用户传递你不处理的nullptr?根本没有必要。此外,您复制T what,这可能很昂贵并且是不必要的工作。这两个参数都应该是 const 引用而不是它们当前的引用。最后,我不知道为什么人们会写if (condition) return true; else return false;,而他们可以写return condition;
          • 感谢您的建议,我当时没有太多经验,同时切换到 Java :) 我更新了评论,让我知道它是否更好或是否还有需要修复的地方。
          • 现在您收到的是引用而不是指针,您需要使用. 而不是-&gt;
          【解决方案15】:

          如果你想在向量中找到一个字符串:

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

          【讨论】:

            【解决方案16】:

            (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

            【讨论】:

              【解决方案17】:

              我个人最近使用模板来一次处理多种类型的容器,而不是只处理向量。我在网上找到了一个类似的例子(不记得在哪里)所以归功于我从谁那里偷来的。这种特殊模式似乎也可以处理原始数组。

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-12-24
                • 2011-07-18
                • 1970-01-01
                • 2019-05-27
                • 1970-01-01
                • 1970-01-01
                • 2015-10-09
                • 2019-07-30
                相关资源
                最近更新 更多