如何检查元素是否在std :: set中?


329

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

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

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

4
比这更简单的唯一方法是布尔谓词:template <typename T> bool member(T const&item)。这将根据您要询问的项目(在幕后)实施。
唐·韦克菲尔德

Answers:


399

典型的检查方法存在许多STL容器,例如std::mapstd::set...是:

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

25
这是特定于布景和地图的。向量,列表等。没有find成员函数。
威廉希尔

8
IMO使用count()更好,因为它更短,并且可以转换为布尔值,如Pieter的答案所述。我不明白为什么这个答案被录取和这么多点...
ОгњенШобајић

4
为了完整起见:向量/列表可以使用std ::发现:std::find(container.begin(), container.end(), element) != container.end(); O(N)问题仍然存在……
阿空加瓜

10
@MichaelMathews使用您的变体:if(container.find(foo) == container.end())需要进行树查找以首先找到元素-如果未找到,则需要进行第二次树查找以找到正确的插入位置。原始变体if(container.insert(foo).second) {...}的魅力在于,只需一个树即可查找...
阿空加瓜

23
有一个set.contains(x)会在C ++ 20标准中返回布尔值。我不知道为什么要到2020
才把

215

简单判断一个元素是否存在的另一种方法是检查 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 
}

102
注意,使用count()代替find()永远不会更好,但可能会更糟。这是因为find()将在第一个匹配项之后返回,count()并将始终遍历所有元素。
弗里希·拉贝

34
@Frerich仅与此有关multisetmultimap我想吗?仍然
Pieter

83
std :: set通常是用有序树结构实现的,因此count()和find()都将具有O(logn)。两者都不会迭代集合中的所有元素。
艾伦2009年

14
@FrerichRaabe-您确定吗?因为只可能set包含一个匹配的成员,所以在这种情况下,是否会像在Pieter指出的那样,在定位第一个元素之后停止该函数的实现?无论如何都有用的答案!
Dan Nissenbaum 2014年

14
@DanNissenbaum是的,您是完全正确的(+ Peter和+ Alan也是如此):对于std :: set,这两个函数在性能上是等效的。因此,即使我的评论的第一部分(count()从未比现在快find())仍然成立,但第二部分确实不适用于std::set。但是,我猜想可以提出另一个论点find():它更具表现力,即强调您要查找元素而不是计算出现次数。
Frerich Raabe 2014年

42

为了明确起见,contains()在这些容器类型中没有像这样的成员的原因是,它使您可以编写效率低下的代码。这样的方法可能只在this->find(key) != this->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 ++ ...;)


44
那套呢?通常你已经拥有的元素,但只是要检查它是否在不在。
埃拉扎尔Leibovich

8
您是否提及这是不是在stl中包含这种方法/功能的实际原因,还是仅仅是您的有根据的猜测?
Fabio A.

3
@FabioA。这是我有根据的猜测。
蒂姆(Tim)

1
@Adhemar,一致性并不是STL的强项...(list::removeremove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich 2014年

3
对我来说,不包含功能是没有意义的,因为如果某人不知道自己在做什么,可能会错误地使用它。编程适用于那些能够自己思考并对其代码及其性能负责的人
slawekwin

13

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

6

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

}

1
@Adhemar,它实际上低效的,但不是在所有你提到的原因。
山姆·哈维尔2009年

@Paul:确保包括的专业化std::set,并记住只有在您唯一需要知道的是存在的情况下才适用。
山姆·哈威尔2009年

@ 280Z28:std :: begin(容器)?那是什么STL标准?它不能在我的gcc上编译。
stefaanv

@stefannv:嘿,它是C ++ 0x的新功能。我在上面的编译器中添加了实现。
山姆·哈威尔2009年

2
@Adhemar:如果您知道集合包含一个值,那么您已经是该值。您需要迭代器的唯一原因是要从集合中删除元素。如果所有你需要的是知道一个集合是否包含一个值,那么这个解决方案比任何其他解决方案没有效率较低。
山姆·哈威尔2009年

4

您还可以在插入元素时检查元素是否在集合中。单个元素版本返回一对,其成员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.

如果新插入的元素比对:: first将指向set中新元素的位置。


2

自己写:

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

4
只是这样做了:template <class T>静态内联布尔包含(const std :: set <T>&S,T x){return(S.find(x)!= S.end()); }
fulmicoton

4
@paul不会创建静态全局函数。而是将函数放在匿名名称空间中:这是C ++创建不会链接到其他编译单元的函数的方法。同样,您的T参数应该是const参考,以确保const正确性和效率。
威廉港

-1:没有模板,也不完全是STL样式。如果您不使用STL,这很好,但是如果您使用STL,则至少应尝试遵循其标准。
山姆·哈威尔2009年

1
@ 280Z28:对不起,我的代码不符合您的标准,我只是在显示,如果您不喜欢STL的界面,则可以编写自己的界面。哎呀,没有模板?它必须如何模板化?您的示例看起来不错,但这并不意味着我的情况不好。正如OP所要求的,它只是更加专注于场景。
stefaanv

1
@ 280Z28:我只是在说一点。我以为人们会足够聪明以获取图片。
stefaanv

2

我用

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

但是它没有效率高

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

我的版本仅节省了编写代码的时间。我更喜欢这种方式进行竞争性编码。


是的count()。在C / C ++习惯用法的大范围内,任何无法掌握布尔表达式中使用的整数返回函数正在测试非零值的人都会遇到很多其他麻烦。而且,如上所述,对于集合而言,确实应该同样有效,这就是问题所在。
罗恩·伯克

0

我能写一般的contains功能std::liststd::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();
富米顿

它不起作用,因为std :: vector需要一个额外的分配器作为模板参数,而std :: set需要一个分配器和一个较少的模板参数。这些行将起作用:template <template <class,class> class STLContainer,class T,class A> bool contains(STLContainer <T,A> container,T elt){return find(container.begin(),container.end( ),elt)!= container.end(); } template <template <template <class,class,class> class STLContainer,class T,L,class A> bool contains(STLContainer <T,A,L> container,T elt){return find(container.begin(),container .end(),elt)!= container.end(); }
tgmath

0

//一般语法

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

/ *在下面的代码中,我试图在元素4中找到元素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.
 }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.