एसोसिएटिव कंटेनर: map, set, unordered_map
ऑर्डर्ड और अनऑर्डर्ड एसोसिएटिव कंटेनर के बारे में जानें — map, set और उनके multi- और unordered वेरिएंट। समझें कि ट्री और हैश टेबल में से कब कौन-सा चुनना है।
ऑर्डर्ड बनाम अनऑर्डर्ड एसोसिएटिव कंटेनर
STL एसोसिएटिव कंटेनर के दो परिवार देता है:
ऑर्डर्ड (रेड-ब्लैक ट्री के रूप में इम्प्लीमेंट किए गए):
- std::map — सॉर्टेड की-वैल्यू जोड़े, यूनिक की
- std::set — सॉर्टेड यूनिक की
- std::multimap — सॉर्टेड की-वैल्यू जोड़े, डुप्लिकेट की मान्य
- std::multiset — सॉर्टेड की, डुप्लिकेट मान्य
अनऑर्डर्ड (हैश टेबल के रूप में इम्प्लीमेंट किए गए):
- std::unordered_map — हैश-आधारित की-वैल्यू, यूनिक की
- std::unordered_set — हैश-आधारित यूनिक की
- std::unordered_multimap — हैश-आधारित, डुप्लिकेट की
- std::unordered_multiset — हैश-आधारित, डुप्लिकेट
ऑर्डर्ड कंटेनर एलिमेंट को सॉर्टेड रखते हैं और O(log n) लुकअप, इन्सर्शन और डिलीशन देते हैं। अनऑर्डर्ड कंटेनर हैशिंग का उपयोग करके औसत स्थिति में O(1) ऑपरेशन देते हैं, लेकिन अगर बहुत ज़्यादा कोलिज़न हों तो सबसे बुरी स्थिति में O(n)।
map और set का उपयोग
std::map की-वैल्यू जोड़ों को की के अनुसार सॉर्ट करके स्टोर करता है। std::set यूनिक सॉर्टेड वैल्यू स्टोर करता है। दोनों सेल्फ़-बैलेंसिंग रेड-ब्लैक ट्री के रूप में इम्प्लीमेंट किए गए हैं, जो O(log n) ऑपरेशन की गारंटी देते हैं।
#include <map>
#include <set>
#include <string>
#include <iostream>
int main() {
// std::map — sorted key-value pairs
std::map<std::string, int> ages;
ages["Alice"] = 30; // insert or update
ages["Bob"] = 25;
ages.insert({"Charlie", 35}); // insert only if key absent
ages.emplace("Diana", 28); // construct in-place
// Lookup
if (auto it = ages.find("Bob"); it != ages.end()) {
std::cout << it->first << " is " << it->second << '\n';
}
// C++17 structured bindings — elegant map iteration
for (const auto& [name, age] : ages) {
std::cout << name << ": " << age << '\n';
}
// Output is sorted by key: Alice, Bob, Charlie, Diana
// std::set — sorted unique values
std::set<int> primes = {2, 3, 5, 7, 11, 13};
primes.insert(5); // no effect — 5 already present
primes.insert(17); // inserted
if (primes.contains(7)) { // C++20
std::cout << "7 is prime\n";
}
// count() — returns 0 or 1 for set (useful pre-C++20)
if (primes.count(4) == 0) {
std::cout << "4 is not prime\n";
}
// lower_bound / upper_bound — range queries
auto lo = primes.lower_bound(5);
auto hi = primes.upper_bound(11);
for (auto it = lo; it != hi; ++it) {
std::cout << *it << ' '; // 5 7 11
}
std::cout << '\n';
}अनऑर्डर्ड कंटेनर और कस्टम हैश फ़ंक्शन
अनऑर्डर्ड कंटेनर औसत स्थिति में O(1) लुकअप के लिए हैश टेबल इस्तेमाल करते हैं। कस्टम की टाइप के लिए आपको एक हैश फ़ंक्शन और एक इक्वैलिटी ऑपरेटर देना ज़रूरी है।
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <iostream>
// Custom type as key
struct Point {
int x, y;
bool operator==(const Point&) const = default; // C++20
};
// Custom hash function
struct PointHash {
std::size_t operator()(const Point& p) const {
// Combine hashes — a common pattern
auto h1 = std::hash<int>{}(p.x);
auto h2 = std::hash<int>{}(p.y);
return h1 ^ (h2 << 1); // simple combine (consider better hashes in production)
}
};
int main() {
// Basic unordered_map — O(1) average lookup
std::unordered_map<std::string, double> prices;
prices["apple"] = 1.50;
prices["banana"] = 0.75;
prices["cherry"] = 3.00;
// try_emplace (C++17) — only constructs value if key is absent
prices.try_emplace("apple", 9.99); // no-op, "apple" exists
prices.try_emplace("date", 4.50); // inserted
// extract and insert (C++17 node API)
auto node = prices.extract("banana");
if (!node.empty()) {
node.key() = "plantain"; // rename key without copy
prices.insert(std::move(node));
}
for (const auto& [fruit, price] : prices) {
std::cout << fruit << ": $" << price << '\n';
}
// Custom type as key
std::unordered_set<Point, PointHash> points;
points.insert({1, 2});
points.insert({3, 4});
points.insert({1, 2}); // duplicate — not inserted
std::cout << "Unique points: " << points.size() << '\n'; // 2
}Multimap और Multiset
std::multimap और std::multiset एक ही की के कई एंट्री रखने की अनुमति देते हैं। किसी दी गई की वाली सभी एंट्री खोजने के लिए equal_range() का उपयोग करें।
#include <map>
#include <set>
#include <iostream>
#include <string>
int main() {
// multimap — multiple values per key
std::multimap<std::string, std::string> phone_book;
phone_book.insert({"Alice", "555-0100"});
phone_book.insert({"Alice", "555-0101"}); // duplicate key OK
phone_book.insert({"Bob", "555-0200"});
// equal_range returns a pair of iterators [first, last)
auto [begin, end] = phone_book.equal_range("Alice");
std::cout << "Alice's numbers:\n";
for (auto it = begin; it != end; ++it) {
std::cout << " " << it->second << '\n';
}
// multiset — count duplicates
std::multiset<int> ms = {1, 2, 2, 3, 3, 3};
std::cout << "Count of 3: " << ms.count(3) << '\n'; // 3
}ऑर्डर्ड कंटेनर के लिए कस्टम कंपेरेटर
ऑर्डर्ड कंटेनर डिफ़ॉल्ट रूप से std::less (आरोही क्रम) इस्तेमाल करते हैं। आप तीसरे टेम्पलेट आर्ग्युमेंट के रूप में कस्टम कंपेरेटर दे सकते हैं:
std::set<int, std::greater<int>> descending = {1, 5, 3};
// इटरेशन का क्रम: 5, 3, 1कस्टम टाइप के लिए, आप या तो अपनी क्लास में operator< परिभाषित कर सकते हैं या कंपेरेटर फ़ंक्टर अथवा लैम्ब्डा पास कर सकते हैं:
auto cmp = [](const Point& a, const Point& b) {
return a.x < b.x || (a.x == b.x && a.y < b.y);
};
std::set<Point, decltype(cmp)> point_set(cmp);C++20 में, आप स्पेसशिप ऑपरेटर (operator<=>) का उपयोग करके सभी कंपैरिज़न ऑपरेटर अपने-आप जनरेट करवा सकते हैं।
std::map या std::unordered_map पर operator[] का उपयोग करने से, अगर की मौजूद नहीं है, तो डिफ़ॉल्ट-कंस्ट्रक्टेड वैल्यू इन्सर्ट हो जाती है। यह सूक्ष्म बग का एक आम स्रोत है:
std::map<std::string, int> m;
int val = m["missing"]; // मैप में {"missing", 0} इन्सर्ट हो जाता है!बिना बदलाव के लुकअप के लिए find(), count() या contains() (C++20) का उपयोग करें। अगर की न मिलने पर आप एक्सेप्शन चाहते हैं, तो at() का उपयोग करें।
unordered_map/unordered_set का उपयोग करें जब आपको सिर्फ़ लुकअप, इन्सर्शन और डिलीशन चाहिए — बड़े कलेक्शन के लिए औसत स्थिति का O(1), O(log n) से बेहतर है।
map/set का उपयोग करें जब आपको चाहिए:
- सॉर्टेड इटरेशन क्रम
- रेंज क्वेरी (lower_bound, upper_bound, equal_range)
- सबसे बुरी स्थिति में भी O(log n) की गारंटी (हैश कोलिज़न की बेहद ख़राब स्थिति का कोई ख़तरा नहीं)
छोटे कलेक्शन (लगभग 100 से कम एलिमेंट) के लिए, std::lower_bound के साथ सॉर्टेड std::vector कैश लोकैलिटी के कारण दोनों से बेहतर प्रदर्शन कर सकता है।
std::mapऔरstd::setरेड-ब्लैक ट्री इस्तेमाल करते हैं — सॉर्टेड क्रम के साथ O(log n) ऑपरेशनstd::unordered_mapऔरstd::unordered_setहैश टेबल इस्तेमाल करते हैं — औसतन O(1), लेकिन हैश फ़ंक्शन ज़रूरी है- मैप के साफ़-सुथरे इटरेशन के लिए C++17 स्ट्रक्चर्ड बाइंडिंग (
auto& [key, val]) का उपयोग करें - मैप पर
operator[]चुपचाप डिफ़ॉल्ट वैल्यू इन्सर्ट कर देता है — सुरक्षित लुकअप के लिएfind()याcontains()का उपयोग करें - कस्टम टाइप को की के रूप में इस्तेमाल करने के लिए
operator<(ऑर्डर्ड) या हैश फ़ंक्शन +operator==(अनऑर्डर्ड) चाहिए try_emplace(C++17) में, अगर की पहले से मौजूद हो, तो वैल्यू कंस्ट्रक्ट नहीं की जाती
क्विज़ — अपना ज्ञान परखें
(15 XP)1. `std::unordered_map` में लुकअप की औसत टाइम कॉम्प्लेक्सिटी क्या है?
2. जब आप `std::map` में मौजूद न होने वाली की के साथ `operator[]` का उपयोग करते हैं, तो क्या होता है?
3. अगर आपको सॉर्टेड इटरेशन और रेंज क्वेरी (lower_bound/upper_bound), दोनों चाहिए, तो आपको कौन-सा कंटेनर चुनना चाहिए?