【问题标题】:How to check that an element is in a std::set?如何检查元素是否在 std::set 中?
【发布时间】:2010-12-14 15:17:30
【问题描述】:

如何检查元素是否在集合中?

是否有以下代码的更简单等效项:

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

【问题讨论】:

  • 唯一比这更简单的方法是布尔谓词:模板 bool member(T const &item)。这将根据您所询问的线路(在幕后)实施。

标签: c++ stl set contains


【解决方案1】:

在许多 STL 容器(例如 std::mapstd::set、...)中检查是否存在的典型方法是:

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

【讨论】:

  • 这是特定于集合和地图的。向量、列表等没有查找成员函数。
  • IMO 使用 count() 更好,因为它更短,并且它转换为 bool,如 Pieter 的回答中所述。我不明白为什么这个答案被接受了这么多点......
  • 为了完整起见:向量/列表可以使用 std::find:std::find(container.begin(), container.end(), element) != container.end();当然,O(N) 问题仍然存在......
  • @MichaelMathews 使用您的变体:if(container.find(foo) == container.end()) 需要先进行树查找以找到元素 - 如果未找到,则需要进行第二次树查找以找到正确的插入位置。原始变体if(container.insert(foo).second) {...} 的魅力在于它只需要一个树查找...
  • 有一个 set.contains(x) 在 C++ 20 标准中返回一个布尔值。我不知道为什么我们要到 2020 年才能做到这一点。
【解决方案2】:

另一种简单判断元素是否存在的方法是检查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 中,set 获得了一个 contains 函数,因此如下所述成为可能:https://stackoverflow.com/a/54197839/895245

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

【讨论】:

  • @Frerich 只与multisetmultimap 相关,我想?不过还是要指出:)
  • std::set 通常使用有序树结构实现,因此 count() 和 find() 都会有 O(logn)。两者都不会遍历集合中的所有元素。
  • @FrerichRaabe - 你确定吗?由于set 只能包含一个匹配的成员,所以在这种情况下,正如 Pieter 指出的那样,该函数是否不会以在找到第一个元素后停止的方式实现?无论如何都是有用的答案!
  • @DanNissenbaum 是的,你完全正确(+Peter 和 +Alan 也是如此):对于 std::set,这两个函数在性能方面是等效的。所以即使我评论的第一部分(count() 永远不会比find() 快)仍然成立,第二部分确实不适用于std::set。但是,我想可以提出另一个支持 find() 的论点:它更具表现力,即强调您正在尝试找到一个元素而不是计算出现次数。
  • 在 GCC .count for set 使用 find: count(const key_type&amp; __x) const { return _M_t.find(__x) == _M_t.end() ? 0 : 1; }
【解决方案3】:

澄清一下,在这些容器类型中没有像contains() 这样的成员的原因是因为它会让你编写低效的代码。这种方法可能只会在内部执行this-&gt;find(key) != this-&gt;end(),但请考虑当密钥确实存在时您会做什么;在大多数情况下,您会想要获取该元素并对其进行处理。这意味着您必须再做一次find(),这是低效的。最好直接使用 find ,这样你就可以缓存你的结果,像这样:

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

当然,如果你不关心效率,你总是可以自己动手,但在这种情况下,你可能不应该使用 C++... ;)

【讨论】:

  • 套装怎么样?通常您已经拥有该元素,但只想检查它是否在其中。
  • 您是否有任何参考,这是否是此类方法/函数未包含在 stl 中的实际原因,还是只是您有根据的猜测?
  • @FabioA。这是我有根据的猜测。
  • 不包含某个功能对我来说没有意义,因为如果有人不知道自己在做什么,他们可能会错误地使用它。编程适合能够独立思考并对代码及其性能负责的人
  • 请注意,C++20 引入了contains()。确实有很多原因,您可能想在没有实际获取迭代器的情况下查看某物是否在集合中。事实上,对于一个集合,除了删除元素之外,你不能对迭代器做太多事情,无论如何你已经可以在没有事先查找的情况下完成。
【解决方案4】:

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

【讨论】:

    【解决方案5】:

    如果您要添加 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));
    }
    

    编辑:

    正如 cmets 中所指出的,我无意中使用了 C++0x 的新函数(std::beginstd::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);
        }
    
    }
    

    【讨论】:

    • @Adhemar,它实际上效率低下,但根本不是因为你提到的原因。
    • @Paul:确保包含std::set 的特化,并记住只有当您需要知道的唯一是存在时才合适。
    • @280Z28: std::begin(container)?那是什么STL标准?它不能在我的 gcc 上编译。
    • @stefannv:嘿,它是 C++0x 的新功能。我从上面的编译器中添加了实现。
    • @Adhemar:如果您知道集合包含一个值,那么您已经是该值。您需要迭代器的唯一原因是从集合中删除元素。如果所有你需要知道一个集合是否包含一个值,那么这个解决方案的效率不亚于任何其他解决方案。
    【解决方案6】:

    您还可以在插入元素时检查元素是否在集合中。 单元素版本返回一个对,其成员 pair::first 设置为一个迭代器,指向新插入的元素或集合中已有的等效元素。如果插入了新元素,则对中的 pair::second 元素设置为 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.
    

    如果元素是新插入的,pair::first 将指向新元素在集合中的位置。

    【讨论】:

      【解决方案7】:

      我用

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

      但效率不如

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

      我的版本只节省了我编写代码的时间。对于竞争性编码,我更喜欢这种方式。

      【讨论】:

      • 是的,count()。任何无法理解布尔表达式中使用的整数返回函数正在测试非零的人都会在 C/C++ 习语的汪洋大海中经历许多其他的艰辛。而且,如上所述,确实应该对集合同样有效,这就是问题所在。
      【解决方案8】:

      从 C++20 开始,有一个简单的(最后!)bool std::contains(const K&amp;) https://en.cppreference.com/w/cpp/container/set/contains

      【讨论】:

        【解决方案9】:

        自己写:

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

        【讨论】:

        • 只是这样做了:模板 static inline bool contains(const std::set& S, T x) { return (S.find(x) != S.end ()); }
        • @paul 不创建静态全局函数。将您的函数放在匿名命名空间中:这是创建不会链接到其他编译单元的函数的 C++ 方式。此外,您的 T 参数应该是一个常量引用,以保证常量的正确性和效率。
        • -1:没有模板化,也没有完全在 STL 样式中。如果您不使用 STL,这很好,但如果您使用的是 STL,您至少应该尝试遵循它的标准。
        • @280Z28: 很抱歉我的代码不符合你们的标准,我只是表明如果您不喜欢 STL 的界面,您可以自己编写。天哪,不是模板吗?它必须如何模板化?您的示例看起来不错,但这并不意味着我的示例不好。正如 OP 所要求的那样,它更专注于场景。
        • @280Z28:我只是在说明一点。我认为人们会足够聪明,能够拍出照片。
        【解决方案10】:

        我能够为std::liststd::vector 编写一个通用的contains 函数,

        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
        

        这会稍微清理一下语法。

        但我无法使用template template parameter magic 使这项工作成为任意 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&lt;typename CONTAINER, typename CONTAINEE&gt; bool contains(const CONTAINER&amp; container, const CONTAINEE&amp; needle) { return find(container.begin(), container.end(), needle) != container.end();
        • 它不起作用,因为 std::vector 需要一个额外的分配器作为模板参数,而 std::set 需要一个分配器和一个 less 模板参数。这些行可以工作: template
        【解决方案11】:

        //一般语法

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

        /* 在下面的代码中,我试图在 int 中找到元素 4,如果它存在与否*/

        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.
         }
        

        【讨论】:

          猜你喜欢
          • 2015-02-14
          • 1970-01-01
          • 2018-01-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-14
          相关资源
          最近更新 更多