C++
एसटीडी :: सेट और एसटी :: मल्टीसेट
खोज…
परिचय
set
एक प्रकार का कंटेनर है, जिसके तत्व क्रमबद्ध और अद्वितीय होते हैं। multiset
समान है, लेकिन, multiset
के मामले में, कई तत्वों का एक ही मूल्य हो सकता है।
टिप्पणियों
उन उदाहरणों में C ++ की विभिन्न शैलियों का उपयोग किया गया है। सावधान रहें कि यदि आप C ++ 98 संकलक का उपयोग कर रहे हैं; इस कोड में से कुछ प्रयोग करने योग्य नहीं हो सकता है।
एक सेट में मूल्यों को सम्मिलित करना
सम्मिलन के तीन अलग-अलग तरीकों का उपयोग सेट के साथ किया जा सकता है।
- सबसे पहले, मूल्य का एक सरल सम्मिलित करें। यह विधि एक जोड़ी देता है जो कॉलर को यह जांचने की अनुमति देता है कि क्या सम्मिलित वास्तव में हुआ था।
- दूसरा, एक संकेत जहां मूल्य डाला जाएगा का संकेत देकर एक सम्मिलित करें। उद्देश्य ऐसे मामले में सम्मिलन समय का अनुकूलन करना है, लेकिन यह जानना कि मूल्य कहां डाला जाना चाहिए, यह सामान्य मामला नहीं है। उस मामले में सावधान रहें; संकेत देने का तरीका संकलक संस्करणों के साथ भिन्न होता है ।
- अंत में आप एक शुरुआत और एक अंत सूचक देकर मूल्यों की एक श्रृंखला सम्मिलित कर सकते हैं। शुरुआती एक को सम्मिलन में शामिल किया जाएगा, समाप्त होने वाले को बाहर रखा गया है।
#include <iostream>
#include <set>
int main ()
{
std::set<int> sut;
std::set<int>::iterator it;
std::pair<std::set<int>::iterator,bool> ret;
// Basic insert
sut.insert(7);
sut.insert(5);
sut.insert(12);
ret = sut.insert(23);
if (ret.second==true)
std::cout << "# 23 has been inserted!" << std::endl;
ret = sut.insert(23); // since it's a set and 23 is already present in it, this insert should fail
if (ret.second==false)
std::cout << "# 23 already present in set!" << std::endl;
// Insert with hint for optimization
it = sut.end();
// This case is optimized for C++11 and above
// For earlier version, point to the element preceding your insertion
sut.insert(it, 30);
// inserting a range of values
std::set<int> sut2;
sut2.insert(20);
sut2.insert(30);
sut2.insert(45);
std::set<int>::iterator itStart = sut2.begin();
std::set<int>::iterator itEnd = sut2.end();
sut.insert (itStart, itEnd); // second iterator is excluded from insertion
std::cout << std::endl << "Set under test contains:" << std::endl;
for (it = sut.begin(); it != sut.end(); ++it)
{
std::cout << *it << std::endl;
}
return 0;
}
आउटपुट होगा:
# 23 has been inserted!
# 23 already present in set!
Set under test contains:
5
7
12
20
23
30
45
एक मल्टीसेट में मूल्यों को सम्मिलित करना
सेट से सभी सम्मिलन विधियाँ मल्टीसेट पर भी लागू होती हैं। फिर भी, एक और संभावना मौजूद है, जो एक initializer_list प्रदान कर रहा है:
auto il = { 7, 5, 12 };
std::multiset<int> msut;
msut.insert(il);
सेट के डिफ़ॉल्ट प्रकार को बदलना
set
और multiset
में डिफ़ॉल्ट तुलना के तरीके हैं, लेकिन कुछ मामलों में आपको उन्हें ओवरलोड करने की आवश्यकता हो सकती है।
आइए कल्पना करें कि हम एक सेट में स्ट्रिंग मान संग्रहीत कर रहे हैं, लेकिन हम जानते हैं कि उन स्ट्रिंग्स में केवल संख्यात्मक मान होते हैं। डिफ़ॉल्ट रूप से सॉर्ट एक lexicographic स्ट्रिंग तुलना होगी, इसलिए यह क्रम संख्यात्मक प्रकार से मेल नहीं खाएगा। यदि आप int
मानों के साथ एक प्रकार लागू करना चाहते हैं, तो आपको तुलना विधि को ओवरलोड करने के लिए एक फ़ंक्टर की आवश्यकता है:
#include <iostream>
#include <set>
#include <stdlib.h>
struct custom_compare final
{
bool operator() (const std::string& left, const std::string& right) const
{
int nLeft = atoi(left.c_str());
int nRight = atoi(right.c_str());
return nLeft < nRight;
}
};
int main ()
{
std::set<std::string> sut({"1", "2", "5", "23", "6", "290"});
std::cout << "### Default sort on std::set<std::string> :" << std::endl;
for (auto &&data: sut)
std::cout << data << std::endl;
std::set<std::string, custom_compare> sut_custom({"1", "2", "5", "23", "6", "290"},
custom_compare{}); //< Compare object optional as its default constructible.
std::cout << std::endl << "### Custom sort on set :" << std::endl;
for (auto &&data : sut_custom)
std::cout << data << std::endl;
auto compare_via_lambda = [](auto &&lhs, auto &&rhs){ return lhs > rhs; };
using set_via_lambda = std::set<std::string, decltype(compare_via_lambda)>;
set_via_lambda sut_reverse_via_lambda({"1", "2", "5", "23", "6", "290"},
compare_via_lambda);
std::cout << std::endl << "### Lambda sort on set :" << std::endl;
for (auto &&data : sut_reverse_via_lambda)
std::cout << data << std::endl;
return 0;
}
आउटपुट होगा:
### Default sort on std::set<std::string> :
1
2
23
290
5
6
### Custom sort on set :
1
2
5
6
23
290
### Lambda sort on set :
6
5
290
23
2
1
ऊपर दिए गए उदाहरण में, कोई भी std::set
तुलना संचालन जोड़ने के 3 अलग-अलग तरीके खोज सकता है, उनमें से प्रत्येक अपने स्वयं के संदर्भ में उपयोगी है।
डिफ़ॉल्ट प्रकार
यह कुंजी (पहले टेम्पलेट तर्क) के तुलना ऑपरेटर का उपयोग करेगा। अक्सर, कुंजी पहले से ही std::less<T>
फ़ंक्शन के लिए एक अच्छा डिफ़ॉल्ट प्रदान करेगी। जब तक यह फ़ंक्शन विशिष्ट नहीं है, यह ऑब्जेक्ट के operator<
का उपयोग करता है। यह विशेष रूप से तब उपयोगी होता है जब अन्य कोड भी कुछ ऑर्डरिंग का उपयोग करने की कोशिश करता है, क्योंकि यह पूरे कोड बेस पर स्थिरता की अनुमति देता है।
कोड को इस तरह से लिखना, आपके कोड को अपडेट करने के प्रयास को कम कर देगा जब कुंजी परिवर्तन एपीआई है, जैसे: 2 सदस्यों वाला एक वर्ग जो 3 सदस्यों वाले वर्ग में बदलता है। operator<
को कक्षा में अपडेट करने से, सभी घटनाएँ अपडेट हो जाएंगी।
जैसा कि आप उम्मीद कर सकते हैं, डिफ़ॉल्ट प्रकार का उपयोग करना एक उचित डिफ़ॉल्ट है।
कस्टम प्रकार
तुलना ऑपरेटर के साथ किसी ऑब्जेक्ट के माध्यम से एक कस्टम प्रकार जोड़ना अक्सर उपयोग किया जाता है जब डिफ़ॉल्ट तुलना अनुपालन नहीं करती है। ऊपर के उदाहरण में यह है क्योंकि तार पूर्णांक की बात कर रहे हैं। अन्य मामलों में, इसका उपयोग अक्सर तब किया जाता है जब आप उस वस्तु के आधार पर (स्मार्ट) पॉइंटर्स की तुलना करना चाहते हैं जो वे संदर्भित करते हैं या क्योंकि आपको तुलना करने के लिए अलग-अलग बाधाओं की आवश्यकता होती है (उदाहरण: std::pair
तुलना std::pair
first
के मूल्य से std::pair
)।
तुलना ऑपरेटर बनाते समय, यह एक स्थिर छँटाई होनी चाहिए। यदि डालने के बाद तुलना ऑपरेटर का परिणाम बदलता है, तो आपके पास अपरिभाषित व्यवहार होगा। एक अच्छे अभ्यास के रूप में, आपके तुलना ऑपरेटर को केवल निरंतर डेटा (कॉस्ट सदस्य, कॉन्स्ट फ़ंक्शन ...) का उपयोग करना चाहिए।
जैसा कि ऊपर दिए गए उदाहरण में है, आप अक्सर ऑपरेटरों की तुलना में सदस्यों के बिना कक्षाओं का सामना करेंगे। इससे डिफ़ॉल्ट कंस्ट्रक्टर और कॉपी कंस्ट्रक्टर बन जाते हैं। डिफॉल्ट कंस्ट्रक्टर आपको निर्माण के समय उदाहरण को छोड़ने की अनुमति देता है और कॉपी कंस्ट्रक्टर की आवश्यकता होती है क्योंकि सेट तुलना ऑपरेटर की एक प्रति लेता है।
लैंबडा प्रकार
लंबदा फ़ंक्शन ऑब्जेक्ट्स लिखने का एक छोटा तरीका है। यह कम ऑपरेटर पर तुलना ऑपरेटर को लिखने की अनुमति देता है, जिससे समग्र कोड अधिक पठनीय हो जाता है।
लैम्ब्डा के उपयोग का नुकसान यह है कि प्रत्येक लैम्ब्डा को संकलन के समय एक विशिष्ट प्रकार मिलता है, इसलिए एक ही संकलन इकाई (सीपीपी फ़ाइल) के प्रत्येक संकलन के लिए decltype(lambda)
अलग-अलग संकलित इकाइयों (जब हेडर फ़ाइल के माध्यम से शामिल होता है) के लिए अलग होगा। )। इस कारण से, इसकी अनुशंसा हेडर फ़ाइलों के भीतर उपयोग किए जाने पर ऑपरेटर की तुलना में फ़ंक्शन ऑब्जेक्ट का उपयोग करने के लिए की जाती है।
इस निर्माण का सामना अक्सर तब किया जाता है जब एक std::set
का उपयोग किसी फ़ंक्शन के स्थानीय दायरे में किया जाता है, जबकि फ़ंक्शन ऑब्जेक्ट को तब पसंद किया जाता है जब फ़ंक्शन तर्क या वर्ग के सदस्यों के रूप में उपयोग किया जाता है।
अन्य प्रकार के विकल्प
जैसा कि std::set
का ऑपरेटर तुलनात्मक तर्क है, सभी कॉल करने योग्य वस्तुओं का उपयोग ऑपरेटर की तुलना में किया जा सकता है और ऊपर दिए गए उदाहरण केवल विशिष्ट मामले हैं। इन कॉल करने योग्य वस्तुओं पर केवल प्रतिबंध हैं:
- उन्हें कॉपी योग्य होना चाहिए
- उन्हें कुंजी के प्रकार के 2 तर्कों के साथ कॉल करने योग्य होना चाहिए। (अंतर्निहित रूपांतरण की अनुमति है, हालांकि अनुशंसित नहीं है क्योंकि यह प्रदर्शन को नुकसान पहुंचा सकता है)
सेट और मल्टीसेट में खोज मूल्य
std::set
या std::multiset
में दिए गए मान को खोजने के कई तरीके हैं:
कुंजी की पहली घटना का पुनरावृत्ति प्राप्त करने के लिए, find()
फ़ंक्शन का उपयोग किया जा सकता है। यदि कुंजी मौजूद नहीं है तो यह end()
देता है।
std::set<int> sut;
sut.insert(10);
sut.insert(15);
sut.insert(22);
sut.insert(3); // contains 3, 10, 15, 22
auto itS = sut.find(10); // the value is found, so *itS == 10
itS = sut.find(555); // the value is not found, so itS == sut.end()
std::multiset<int> msut;
sut.insert(10);
sut.insert(15);
sut.insert(22);
sut.insert(15);
sut.insert(3); // contains 3, 10, 15, 15, 22
auto itMS = msut.find(10);
एक अन्य तरीका count()
फ़ंक्शन का उपयोग कर रहा है, जो गिनता है कि set
/ multiset
में कितने संबंधित मान पाए गए हैं ( set
मामले में, वापसी मूल्य केवल 0 या 1 हो सकता है)। उपरोक्त मानों का उपयोग करते हुए, हमारे पास होगा:
int result = sut.count(10); // result == 1
result = sut.count(555); // result == 0
result = msut.count(10); // result == 1
result = msut.count(15); // result == 2
std::multiset
के मामले में, समान मूल्य वाले कई तत्व हो सकते हैं। इस श्रेणी को प्राप्त करने के लिए, equal_range()
फ़ंक्शन का उपयोग किया जा सकता है। यह क्रमशः std::pair
को देता है जिसमें निम्नतर बाउंड (समावेशी) और ऊपरी बाउंड (अनन्य) होता है। यदि कुंजी मौजूद नहीं है, तो दोनों पुनरावृत्तियां निकटतम श्रेष्ठ मान (दिए गए multiset
को छांटने के लिए उपयोग की जाने वाली तुलना पद्धति के आधार पर) को इंगित करेंगी।
auto eqr = msut.equal_range(15);
auto st = eqr.first; // point to first element '15'
auto en = eqr.second; // point to element '22'
eqr = msut.equal_range(9); // both eqr.first and eqr.second point to element '10'
एक सेट से मान हटाना
सबसे स्पष्ट विधि, यदि आप अपना सेट / मल्टीसेट रीसेट करना चाहते हैं, तो clear
उपयोग करें:
std::set<int> sut;
sut.insert(10);
sut.insert(15);
sut.insert(22);
sut.insert(3);
sut.clear(); //size of sut is 0
तब erase
विधि का उपयोग किया जा सकता है। यह कुछ संभावनाएं प्रदान करता है जो प्रविष्टि के समतुल्य हैं:
std::set<int> sut;
std::set<int>::iterator it;
sut.insert(10);
sut.insert(15);
sut.insert(22);
sut.insert(3);
sut.insert(30);
sut.insert(33);
sut.insert(45);
// Basic deletion
sut.erase(3);
// Using iterator
it = sut.find(22);
sut.erase(it);
// Deleting a range of values
it = sut.find(33);
sut.erase(it, sut.end());
std::cout << std::endl << "Set under test contains:" << std::endl;
for (it = sut.begin(); it != sut.end(); ++it)
{
std::cout << *it << std::endl;
}
आउटपुट होगा:
Set under test contains:
10
15
30
वे सभी विधियां multiset
भी लागू होती हैं। कृपया ध्यान दें कि यदि आप एक multiset
से एक तत्व को हटाने के लिए कहते हैं, और यह कई बार मौजूद है, तो सभी समान मान हटा दिए जाएंगे ।