วิธีการตรวจสอบว่าองค์ประกอบอยู่ใน std :: set?


329

คุณจะตรวจสอบว่าองค์ประกอบอยู่ในชุดได้อย่างไร

มีรหัสต่อไปนี้ที่ง่ายกว่านี้หรือไม่:

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

4
วิธีเดียวที่จะทำให้ง่ายกว่านั้นคือเพรดิเคตบูลีน: เทมเพลต <typename T> สมาชิกบูล (T const & item) และนั่นจะถูกนำมาใช้ (ภายใต้หน้าปก) ในแง่ของบรรทัดที่คุณกำลังถาม
Don Wakefield

คำตอบ:


399

วิธีปกติในการตรวจสอบสำหรับการดำรงอยู่ในภาชนะบรรจุที่ STL หลายอย่างเช่นstd::map, std::set... คือ

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

25
นี่เป็นข้อมูลเฉพาะสำหรับชุดและแผนที่ รายการเวกเตอร์ ฯลฯ ไม่มีฟังก์ชันค้นหาสมาชิก
wilhelmtell

8
IMO ที่ใช้ count () ดีกว่าเพราะสั้นกว่าและแปลงเป็นบูลดังที่ระบุไว้ในคำตอบโดย Pieter ฉันไม่เข้าใจว่าทำไมคำตอบนี้จึงได้รับการยอมรับและมีคะแนนมากมาย ...
ОгњенШобајић

4
เพื่อประโยชน์ของความสมบูรณ์: เวกเตอร์ / รายการสามารถใช้มาตรฐาน :: พบ: std::find(container.begin(), container.end(), element) != container.end(); ยังคงมีปัญหา O (N) แน่นอน ...
Aconcagua

10
@MichaelMathews กับตัวแปรของคุณ: if(container.find(foo) == container.end())ต้องทำการค้นหาต้นไม้เพื่อค้นหาองค์ประกอบแรก - หากไม่พบคุณจะต้องทำการค้นหาต้นไม้ลำดับที่สองเพื่อค้นหาตำแหน่งการแทรกที่ถูกต้อง ตัวแปรดั้งเดิมif(container.insert(foo).second) {...}มีเสน่ห์ที่มันต้องการเพียงการค้นหาต้นไม้เดี่ยวเดียว ...
Aconcagua

23
มี a set.contains(x)ที่คืนค่าบูลในมาตรฐาน C ++ 20 ฉันไม่รู้ว่าทำไมเราถึงต้องใช้เวลาจนถึงปี 2020 เพื่อรับสิ่งนั้น
gremwell

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 จะได้รับcontainsฟังก์ชันดังนั้นจึงเป็นไปได้ดังต่อไปนี้ตามที่ระบุไว้ที่: https://stackoverflow.com/a/54197839/895245

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

102
โปรดทราบว่าการใช้count()แทนfind()จะไม่ดีกว่า แต่อาจแย่กว่านั้น นี่เป็นเพราะfind()จะกลับมาหลังจากการแข่งขันครั้งแรกcount()จะซ้ำกับองค์ประกอบทั้งหมดเสมอ
Frerich Raabe

34
@ Frerich ที่เกี่ยวข้องเท่านั้นmultisetและmultimapฉันคิดว่า? ยังดีที่จะชี้ให้เห็นว่า :)
ปีเตอร์

83
std :: set ถูกนำไปใช้กับโครงสร้างแบบต้นไม้ที่สั่งไว้ดังนั้น count () และ find () ทั้งสองจะมี O (logn) จะไม่วนซ้ำองค์ประกอบทั้งหมดในชุด
อลัน

14
@FercherRaabe - คุณแน่ใจหรือ เนื่องจากเป็นไปได้เพียงเพื่อsetให้มีสมาชิกหนึ่งคนที่ตรงกันฟังก์ชันจะไม่ถูกนำไปใช้ในลักษณะที่จะหยุดหลังจากค้นหาองค์ประกอบแรกในกรณีนี้เมื่อปีเตอร์ชี้ให้เห็น? คำตอบที่เป็นประโยชน์ในกรณีใด ๆ !
Dan Nissenbaum

14
@DanNissenbaum ใช่คุณพูดถูก (และ + ปีเตอร์กับ + อลัน): สำหรับ std :: set ทั้งสองฟังก์ชั่นนั้นเทียบเท่ากับประสิทธิภาพ ดังนั้นแม้ว่าส่วนแรกของความคิดเห็นของฉัน ( count()ไม่เร็วกว่าfind()) ยังคงมีอยู่ส่วนที่สองก็ไม่สามารถใช้ได้std::setจริง อย่างไรก็ตามฉันเดาว่าสามารถสร้างข้อโต้แย้งอีกอย่างหนึ่งได้ว่าfind(): มันแสดงออกได้มากกว่านั่นคือเน้นว่าคุณกำลังพยายามหาองค์ประกอบแทนที่จะนับจำนวนครั้ง
Frerich Raabe

42

เพื่อชี้แจงเหตุผลที่ไม่มีสมาชิกcontains()ในประเภทภาชนะเหล่านี้เป็นเพราะมันจะเปิดให้คุณเขียนโค้ดที่ไม่มีประสิทธิภาพ วิธีการดังกล่าวอาจจะทำthis->find(key) != this->end()ภายใน แต่พิจารณาสิ่งที่คุณทำเมื่อคีย์มีอยู่จริง ในกรณีส่วนใหญ่คุณจะต้องการรับองค์ประกอบและทำบางสิ่งกับมัน ซึ่งหมายความว่าคุณต้องทำวินาที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
แล้วชุดล่ะ? โดยปกติคุณจะมีองค์ประกอบอยู่แล้ว แต่ต้องการตรวจสอบว่ามีอยู่หรือไม่
Elazar Leibovich

8
คุณมีการอ้างอิงใด ๆ ว่านี่เป็นเหตุผลที่แท้จริงเช่นวิธีการ / ฟังก์ชั่นไม่รวมอยู่ใน stl หรือมันเป็นเพียงการคาดเดาการศึกษาของคุณ?
Fabio A.

3
@FabioA มันเป็นการเดาที่มีการศึกษาของฉัน
ทิม

1
@Adhemar ความสอดคล้องไม่ได้เป็นด้านที่แข็งแกร่งของ STL ... ( list::remove, remove(makes_sense_only_for_vector, iterators)... )
Elazar Leibovich

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::beginและstd::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 ก็จริงก็ไม่มีประสิทธิภาพ แต่ไม่ได้ทุกเหตุผลที่คุณกล่าวถึง
Sam Harwell

@ พอล: ตรวจสอบให้แน่ใจว่าคุณมีความเชี่ยวชาญเฉพาะด้านstd::setและโปรดจำไว้ว่ามันเหมาะสมถ้าสิ่งเดียวที่คุณต้องรู้คือการมีอยู่
Sam Harwell

@ 280Z28: มาตรฐาน :: เริ่มต้น (คอนเทนเนอร์)? มาตรฐาน STL นั้นคืออะไร? มันไม่ได้รวบรวมใน gcc ของฉัน
stefaanv

@stefannv: เฮ้มันใหม่สำหรับ C ++ 0x ฉันเพิ่มการใช้งานจากคอมไพเลอร์ของฉันด้านบน
Sam Harwell

2
@Adhemar: ถ้าคุณรู้ว่าชุดมีค่าแสดงว่าคุณมีค่าอยู่แล้ว เหตุผลเดียวที่คุณต้องการตัววนซ้ำคือการลบองค์ประกอบออกจากชุด หากทั้งหมดที่คุณต้องการจะรู้หรือไม่ว่าคอลเลกชันมีค่าแล้ววิธีนี้คือการไม่มีประสิทธิภาพน้อยกว่าวิธีอื่น ๆ
Sam Harwell

4

นอกจากนี้คุณยังสามารถตรวจสอบว่าองค์ประกอบอยู่ในชุดหรือไม่ในขณะที่แทรกองค์ประกอบ รุ่นองค์ประกอบเดียวส่งคืนคู่โดยมีสมาชิกเป็นคู่ :: ชุดแรกเป็นตัววนซ้ำชี้ไปที่องค์ประกอบที่เพิ่งแทรกใหม่หรือองค์ประกอบที่เทียบเท่าในชุด คู่ :: องค์ประกอบที่สองในคู่ตั้งเป็นจริงถ้าองค์ประกอบใหม่ถูกแทรกหรือเท็จถ้าองค์ประกอบเทียบเท่าอยู่แล้ว

ตัวอย่างเช่นสมมติว่าชุดนั้นมี 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.

หากองค์ประกอบถูกแทรกใหม่กว่าคู่ :: ก่อนจะชี้ไปที่ตำแหน่งขององค์ประกอบใหม่ในชุด


2

เขียนของคุณเอง:

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

4
เพิ่งทำเช่นนั้น: เทมเพลต <คลาส T> static inline bool มี (const std :: set <T> & S, T x) {return (S.find (x)! = S.end ()); }
fulmicoton

4
@paul ไม่ได้สร้างฟังก์ชั่นระดับโลกคงที่ ใส่ฟังก์ชั่นของคุณในเนมสเปซที่ไม่ระบุชื่อแทน: นั่นคือวิธีการสร้างฟังก์ชั่น C ++ ที่จะไม่เชื่อมโยงไปยังหน่วยการคอมไพล์อื่น ๆ นอกจากนี้พารามิเตอร์ T ของคุณควรอ้างอิง const สำหรับความถูกต้อง const และเพื่อประสิทธิภาพ
wilhelmtell

-1: ไม่ใช่เทมเพลตและไม่ได้อยู่ในรูปแบบ STL สิ่งนี้ดีถ้าคุณไม่ได้ใช้ STL แต่ถ้าคุณกำลังใช้ STL อย่างน้อยคุณควรพยายามที่จะปฏิบัติตามมาตรฐาน
Sam Harwell

1
@ 280Z28: ฉันขอโทษที่รหัสของฉันไม่ได้มาตรฐานของคุณฉันแค่แสดงให้เห็นว่าถ้าคุณไม่ชอบอินเตอร์เฟซของ STL คุณสามารถเขียนของคุณเอง Jeez ไม่ใช่เทมเพลตเหรอ? มันต้องมีเทมเพลตอย่างไร? ตัวอย่างของคุณดูดีนั่นไม่ได้หมายความว่าของฉันไม่ดี มันมุ่งเน้นไปที่ฉากที่ถูกถามโดย 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 ++ และตามที่ระบุไว้ข้างต้นจริงๆควรมีประสิทธิภาพสำหรับชุดซึ่งเป็นคำถาม
Ron Burk

0

ผมสามารถที่จะเขียนทั่วไปcontainsฟังก์ชั่นstd::listและstd::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();
fulmicoton

ไม่ทำงานเนื่องจาก std :: vector ต้องการตัวจัดสรรเพิ่มเติมเนื่องจากอาร์กิวเมนต์ของแม่แบบและ std :: set ต้องการตัวจัดสรรและอาร์กิวเมนต์ของแม่แบบที่น้อยลง บรรทัดเหล่านี้จะทำงาน: template <template <class, class> class STLContainer, คลาส T, คลาส A> bool มี (STLContainer <T, A> คอนเทนเนอร์, T elt) {return find (container.begin (), container.end ( ), elt)! = container.end (); } template <template <class, class, class> class STLContainer, คลาส T, คลาส L, คลาส A> bool มี (STLContainer <T, A, L> คอนเทนเนอร์, T elt) {return find (container.begin (), คอนเทนเนอร์ .end (), elt)! = container.end (); }
tgmath

0

// ไวยากรณ์ทั่วไป

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

/ * ในโค้ดด้านล่างฉันพยายามค้นหาอิลิเมนต์ 4 in และ int set หากมีอยู่หรือไม่ * /

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.
 }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.