Làm cách nào để kiểm tra một phần tử có trong std :: set không?


328

Làm thế nào để bạn kiểm tra xem một phần tử có trong một tập hợp không?

Có tương đương đơn giản hơn của mã sau đây:

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

4
Cách duy nhất để có được đơn giản hơn đó là một vị từ boolean: template <typename T> bool thành viên (T const & item). Và điều đó sẽ được thực hiện (dưới vỏ bọc) theo dòng bạn đang hỏi về.
Don Wakefield

Câu trả lời:


397

Cách thông thường để kiểm tra sự tồn tại trong nhiều container STL như std::map, std::set, ... là:

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

24
Điều này là cụ thể cho các bộ và bản đồ. vectơ, danh sách, vv không có chức năng tìm thành viên.
wilmustell

8
IMO sử dụng Count () tốt hơn bởi vì nó đơn giản là ngắn hơn và nó chuyển đổi thành bool như được ghi chú trong câu trả lời của Pieter. Tôi không hiểu tại sao câu trả lời này được chấp nhận và rất nhiều điểm ...
ОШ

4
Để hoàn thiện: vectơ / danh sách có thể sử dụng std :: find : std::find(container.begin(), container.end(), element) != container.end(); Vấn đề O (N) vẫn còn, tất nhiên ...
Aconcagua

10
@MichaelMathews Với biến thể của bạn: if(container.find(foo) == container.end())cần thực hiện tra cứu cây để tìm phần tử trước - nếu không tìm thấy, thì bạn cần thực hiện tra cứu cây thứ hai để tìm vị trí chèn chính xác. Biến thể ban đầu if(container.insert(foo).second) {...}có sức hấp dẫn là nó chỉ cần một lần tra cứu cây ...
Aconcagua

23
có một set.contains(x)trả về một bool trong tiêu chuẩn C ++ 20. Tôi không biết tại sao chúng tôi phải mất đến năm 2020 để có được điều đó.
gremwell

213

Một cách khác để chỉ đơn giản là nếu một phần tử tồn tại là kiểm tra count()

if (myset.count(x)) {
   // x is in the set, count is 1
} else {
   // count zero, i.e. x not in the set
}

Tuy nhiên, hầu hết thời gian, tôi thấy mình cần truy cập vào phần tử bất cứ nơi nào tôi kiểm tra sự tồn tại của nó.

Vì vậy, tôi phải tìm iterator nào. Sau đó, tất nhiên, tốt hơn là chỉ đơn giản so sánh nó với endquá.

set< X >::iterator it = myset.find(x);
if (it != myset.end()) {
   // do something with *it
}

C ++ 20

Trong C ++ 20, bộ có một containschức năng, do đó, những điều sau đây có thể được đề cập tại: https://stackoverflow.com/a/54197839/895245

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

101
Lưu ý rằng sử dụng count()thay vì find()không bao giờ tốt hơn nhưng có khả năng tồi tệ hơn. Điều này là bởi vì find()sẽ trở lại sau trận đấu đầu tiên, count()sẽ luôn lặp đi lặp lại trên tất cả các yếu tố.
Frerich Raabe

34
@Frerich chỉ liên quan multisetmultimaptôi nghĩ sao? Vẫn tốt để chỉ ra mặc dù :)
Pieter

83
std :: set thường được triển khai với cấu trúc cây theo thứ tự, do đó, cả Count () và find () sẽ có O (logn). Không lặp lại tất cả các yếu tố trong tập hợp.
Alan

14
@FrerichRaabe - Bạn có chắc không? Vì chỉ có thể setchứa một thành viên phù hợp, nên chức năng sẽ không được thực hiện theo cách dừng lại sau khi định vị phần tử đầu tiên, trong trường hợp này, như Pieter chỉ ra? Câu trả lời hữu ích trong mọi trường hợp!
Dan Nissenbaum

14
@DanNissenbaum Vâng, bạn hoàn toàn chính xác (và cũng vậy + Peter và + Alan): đối với std :: set, hai hàm tương đương nhau về hiệu suất. Vì vậy, mặc dù phần đầu tiên trong nhận xét của tôi ( count()không bao giờ nhanh hơn find()) vẫn giữ, phần thứ hai thực sự không áp dụng được std::set. Tuy nhiên, tôi đoán một lập luận khác có thể được đưa ra có lợi cho find(): đó là biểu cảm hơn, tức là nhấn mạnh rằng bạn đang cố gắng tìm một yếu tố thay vì đếm số lần xuất hiện.
Frerich Raabe

42

Chỉ cần làm rõ, lý do tại sao không có thành viên như contains()trong các loại container này là bởi vì nó sẽ mở ra cho bạn để viết mã không hiệu quả. Một phương pháp như vậy có thể chỉ là thực hiện this->find(key) != this->end()nội bộ, nhưng hãy xem xét những gì bạn làm khi chìa khóa thực sự có mặt; trong hầu hết các trường hợp, sau đó bạn sẽ muốn lấy phần tử và làm một cái gì đó với nó. Điều này có nghĩa là bạn phải làm một giây find(), không hiệu quả. Tốt hơn là sử dụng find trực tiếp, vì vậy bạn có thể lưu trữ kết quả của mình, như vậy:

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

Tất nhiên, nếu bạn không quan tâm đến hiệu quả, bạn luôn có thể tự lăn, nhưng trong trường hợp đó có lẽ bạn không nên sử dụng C ++ ...;)


44
Còn bộ thì sao? Thông thường bạn đã có yếu tố này, nhưng chỉ muốn kiểm tra xem nó có hoạt động không.
Elazar Leibovich

8
Bạn có bất kỳ tài liệu tham khảo nào cho dù đây là lý do thực tế của một phương thức / chức năng như vậy không được bao gồm trong stl, hay đó chỉ là phỏng đoán có giáo dục của bạn?
Fabio A.

3
@FabioA. Đó là phỏng đoán có giáo dục của tôi.
Tim

1
@Adhemar, tính nhất quán không chính xác là mặt mạnh của STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)...)
Elazar Leibovich

3
Nó không có nghĩa đối với tôi không bao gồm một tính năng bởi vì ai đó có thể sử dụng nó không chính xác nếu họ không biết những gì họ đang làm. Lập trình dành cho những người có thể tự suy nghĩ và chịu trách nhiệm về mã của họ và hiệu suất của nó
slawekwin

13

Trong C ++ 20 cuối cùng chúng ta cũng sẽ có được std::set::containsphương thức.

#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

Nếu bạn định thêm một containschức năng, nó có thể trông như thế này:

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

Điều này hoạt động với std::set, các container STL khác và thậm chí các mảng có độ dài cố định:

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

Biên tập:

Như đã chỉ ra trong các bình luận, tôi đã vô tình sử dụng một chức năng mới cho C ++ 0x ( std::beginstd::end). Đây là cách triển khai gần như tầm thường từ 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, nó thực sự không hiệu quả, nhưng không phải ở tất cả vì lý do bạn đề cập đến.
Sam Harwell

@Paul: Đảm bảo rằng bạn bao gồm chuyên môn cho std::setvà nhớ rằng nó chỉ phù hợp nếu điều duy nhất bạn cần biết là sự tồn tại.
Sam Harwell

@ 280Z28: std :: started (container)? Tiêu chuẩn STL là gì? Nó không biên dịch trên gcc của tôi.
stefaanv

@stefannv: heh, nó mới cho C ++ 0x. Tôi đã thêm việc thực hiện từ trình biên dịch của tôi ở trên.
Sam Harwell

2
@Adhemar: Nếu bạn biết tập hợp chứa một giá trị, thì bạn đã là giá trị. Lý do duy nhất bạn cần iterator là xóa phần tử khỏi tập hợp. Nếu tất cả những gì bạn cần là để biết liệu một bộ sưu tập có chứa một giá trị hay không, thì giải pháp này không kém hiệu quả so với bất kỳ giải pháp nào khác.
Sam Harwell

4

Bạn cũng có thể kiểm tra xem một phần tử có được thiết lập hay không trong khi chèn phần tử. Phiên bản phần tử đơn trả về một cặp, với cặp thành viên của nó :: đầu tiên được đặt thành một trình vòng lặp trỏ đến phần tử mới được chèn hoặc phần tử tương đương đã có trong tập hợp. Phần tử cặp :: thứ hai trong cặp được đặt thành đúng nếu phần tử mới được chèn hoặc sai nếu phần tử tương đương đã tồn tại.

Ví dụ: Giả sử tập hợp đã có 20 là một phần tử.

 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.

Nếu phần tử mới được chèn hơn cặp :: đầu tiên sẽ trỏ đến vị trí của phần tử mới trong tập hợp.


2

Viết của riêng bạn:

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

4
vừa làm như vậy: template <class T> static inline bool chứa (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
Fulmicoton

4
@paul không tạo các hàm toàn cầu tĩnh. thay vào đó, hãy đặt hàm của bạn vào một không gian tên ẩn danh: đó là cách tạo các hàm C ++ không liên kết với các đơn vị biên dịch khác. Ngoài ra, tham số T của bạn phải là một tham chiếu const, cho tính chính xác và hiệu quả.
wilmustell

-1: Không templated và không ở tất cả trong phong cách STL. Điều này tốt nếu bạn không sử dụng STL, nhưng nếu bạn đang sử dụng STL, ít nhất bạn nên cố gắng tuân theo các tiêu chuẩn của nó.
Sam Harwell

1
@ 280Z28: Tôi xin lỗi vì mã của tôi không theo tiêu chuẩn của bạn, tôi chỉ cho thấy rằng nếu bạn không thích giao diện của STL, bạn có thể tự viết. Jeez, không templated? Làm thế nào templated nó phải được? Ví dụ của bạn có vẻ tốt, điều đó không có nghĩa là tôi xấu. Nó chỉ tập trung hơn vào bộ ảnh như đã được OP yêu cầu.
stefaanv

1
@ 280Z28: Tôi chỉ đang đưa ra quan điểm. Tôi nghĩ rằng mọi người sẽ đủ thông minh để có được bức ảnh.
stefaanv

2

tôi sử dụng

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

Nhưng nó không hiệu quả bằng

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

Phiên bản của tôi chỉ tiết kiệm thời gian của tôi bằng cách viết mã. Tôi thích nó theo cách này để mã hóa cạnh tranh.


Vâng, count(). Bất kỳ ai cũng không thể hiểu rằng hàm trả về số nguyên được sử dụng trong biểu thức Boolean đang kiểm tra khác không sẽ có nhiều, nhiều chuyến đi khác trong biển thành ngữ C / C ++ tuyệt vời. Và, như đã lưu ý ở trên, thực sự nên có hiệu quả cho các bộ, đó là câu hỏi.
Ron Burk

0

Tôi đã có thể viết một containshàm chung cho 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

Điều này làm sạch cú pháp một chút.

Nhưng tôi không thể sử dụng ma thuật tham số mẫu để làm cho công việc này chứa các thùng chứa stl tùy ý.

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

Bất kỳ ý kiến ​​về việc cải thiện câu trả lời cuối cùng sẽ được tốt đẹp.


Xin lỗi, tôi thực sự không thể viết mã khối trong bình luận nhưng về cái gì template<typename CONTAINER, typename CONTAINEE> bool contains(const CONTAINER& container, const CONTAINEE& needle) { return find(container.begin(), container.end(), needle) != container.end();
Fulmicoton

Nó không hoạt động, bởi vì std :: vector cần một bộ cấp phát bổ sung làm đối số khuôn mẫu và std :: set cần một bộ cấp phát và một đối số mẫu ít hơn. Các dòng woulkd này hoạt động: template <template <class, class> class STLContainer, class T, class A> bool chứa (STLContainer <T, A> container, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } template <template <class, class, class> class STLContainer, class T, class L, class A> bool chứa (STLContainer <T, A, L> container, T elt) {return find (container.begin (), container .end (), elt)! = container.end (); }
tgmath

0

// Cú pháp chung

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

/ * trong đoạn mã dưới đây tôi đang cố gắng tìm phần tử 4 trong và int set nếu nó có mặt hay không * /

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.
 }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.