C ++ में HashMap का उपयोग करने का सबसे अच्छा तरीका क्या है?


174

मुझे पता है कि STL के पास एक HashMap API है, लेकिन मैं इस बारे में अच्छे उदाहरणों के साथ कोई अच्छा और संपूर्ण दस्तावेज नहीं ढूँढ सकता।

किसी भी अच्छे उदाहरण की सराहना की जाएगी।


क्या आप C ++ 1x हैश_मैप, या std :: मैप के बारे में पूछ रहे हैं?
दार्शनिक

2
मैं C ++ में java.util.HashMap की तरह कुछ चाहता हूं और अगर कोई एक है तो इसे करने के लिए स्टैंडराइज्ड तरीका। सबसे अच्छा गैर मानक पुस्तकालय। जब वे HashMap की आवश्यकता होती है तो C ++ डेवलपर्स आमतौर पर क्या उपयोग करते हैं?
user855

जवाबों:


237

मानक लाइब्रेरी में ऑर्डर किए गए और बिना नक्शे वाले ( std::mapऔर std::unordered_map) कंटेनर शामिल हैं। एक ऑर्डर किए गए नक्शे में तत्वों को कुंजी द्वारा सॉर्ट किया जाता है, डालने और एक्सेस ओ (लॉग एन) में होता है । आमतौर पर मानक पुस्तकालय आंतरिक रूप से आदेशित नक्शों के लिए लाल काले पेड़ों का उपयोग करता है । लेकिन यह सिर्फ एक कार्यान्वयन विवरण है। एक अनियंत्रित मानचित्र में डालने और पहुँच O (1) में है। हैशटेबल के लिए यह सिर्फ एक और नाम है।

(आदेशित) के साथ एक उदाहरण std::map:

#include <map>
#include <iostream>
#include <cassert>

int main(int argc, char **argv)
{
  std::map<std::string, int> m;
  m["hello"] = 23;
  // check if key is present
  if (m.find("world") != m.end())
    std::cout << "map contains key world!\n";
  // retrieve
  std::cout << m["hello"] << '\n';
  std::map<std::string, int>::iterator i = m.find("hello");
  assert(i != m.end());
  std::cout << "Key: " << i->first << " Value: " << i->second << '\n';
  return 0;
}

आउटपुट:

23
कुंजी: हैलो मान: 23

यदि आपको अपने कंटेनर में ऑर्डर करने की आवश्यकता है और ओ (लॉग एन) रनटाइम के साथ ठीक है तो बस उपयोग करें std::map

अन्यथा, यदि आपको वास्तव में हैश-टेबल (ओ (1) इंसर्ट / एक्सेस) की आवश्यकता है, तो देखें कि एपीआई के std::unordered_mapसमान है std::map(उदाहरण के लिए ऊपर दिए गए उदाहरण में आपको बस खोज करना है और उसके mapसाथ बदलना है unordered_map)।

unordered_mapकंटेनर के साथ पेश किया गया था सी ++ 11 मानक संशोधन। इस प्रकार, आपके कंपाइलर के आधार पर, आपको C ++ 11 सुविधाओं को सक्षम करना होगा (जैसे GCC 4.8 का उपयोग करते समय आपको -std=c++11CXXFLAGS में जोड़ना होगा)।

C ++ 11 रिलीज़ से पहले ही GCC समर्थित unordered_map- नेमस्पेस में std::tr1। इस प्रकार, पुराने जीसीसी संकलक के लिए आप इसे इस तरह उपयोग करने का प्रयास कर सकते हैं:

#include <tr1/unordered_map>

std::tr1::unordered_map<std::string, int> m;

यह भी बढ़ावा देने का हिस्सा है, यानी आप बेहतर पोर्टेबिलिटी के लिए संबंधित बूस्टर-हेडर का उपयोग कर सकते हैं ।


1
जबकि मानक पुस्तकालय एक हैश तालिका के आधार पर कंटेनर नहीं है, लगभग सभी कार्यान्वयन में शामिल कुछ या किसी अन्य रूप में एसजीआई एसटीएल से। hash_map
जेम्स मैकनेलिस

@JamesMcNellis जिसे HashMap कार्यान्वयन के लिए unordered_map या hash_map की सलाह दी जाती है
शामील मोहम्मद

2
@ShameelMohamed, 2017, यानी C ++ 11 के 6 साल बाद ऐसा STL ढूंढना कठिन होना चाहिए जो प्रदान नहीं करता है unordered_map। इस प्रकार, गैर-मानक पर विचार करने का कोई कारण नहीं है hash_map
मैक्सक्लेपजिग

30

A hash_map, मानकीकरण के उद्देश्यों के लिए जो कुछ भी करता है उसका एक पुराना, अविकसित संस्करण है unordered_map(मूल रूप से TR1 में, और C ++ 11 के बाद से मानक में शामिल है)। जैसा कि नाम से पता चलता है, यह std::mapमुख्य रूप से अनियंत्रित होने से अलग है - यदि, उदाहरण के लिए, आप से मैप के माध्यम से पुनरावृति begin()करते हैं end(), तो आपको कुंजी 1 से क्रम में आइटम मिलते हैं , लेकिन यदि आप से unordered_mapसे के माध्यम से पुनरावृत्ति begin()करते हैं end(), तो आपको आइटम मिलते हैं अधिक या कम मनमाना क्रम।

एक unordered_mapसामान्य रूप से निरंतर जटिलता होने की उम्मीद है। अर्थात्, एक प्रविष्टि, लुकअप, आदि, आमतौर पर तालिका में कितने आइटम हैं, चाहे वे निश्चित रूप से समय की एक निश्चित राशि लेते हैं। एक std::mapजटिलता है जो संग्रहीत किए जा रहे आइटमों की संख्या पर लघुगणक है - जिसका अर्थ है कि किसी आइटम को सम्मिलित करने या पुनर्प्राप्त करने का समय बढ़ता है, लेकिन धीरे-धीरे , जैसा कि मानचित्र बड़ा होता है। उदाहरण के लिए, यदि 1 मिलियन वस्तुओं में से एक को देखने के लिए 1 माइक्रोसेकंड लेता है, तो आप 2 मिलियन आइटमों में से किसी एक को देखने के लिए 2 माइक्रोसेकंड ले सकते हैं, 4 मिलियन आइटमों में से एक के लिए 3 माइक्रोसेकंड, 8 मिलियन में से एक के लिए 4 माइक्रोसेकंड आइटम, आदि

एक व्यावहारिक दृष्टिकोण से, यह वास्तव में पूरी कहानी नहीं है। स्वभाव से, एक साधारण हैश तालिका का एक निश्चित आकार होता है। एक सामान्य प्रयोजन कंटेनर के लिए चर-आकार की आवश्यकताओं के लिए इसे अपनाना कुछ हद तक गैर-तुच्छ है। नतीजतन, संचालन जो (संभावित रूप से) तालिका को बढ़ाते हैं (जैसे, सम्मिलन) संभावित रूप से अपेक्षाकृत धीमी गति से होते हैं (यानी, ज्यादातर काफी तेज होते हैं, लेकिन समय-समय पर एक बहुत धीमी हो जाएगी)। लुकअप, जो तालिका का आकार नहीं बदल सकता है, आम तौर पर बहुत तेज होते हैं। परिणामस्वरूप, जब आप सम्मिलन की संख्या की तुलना में बहुत सारे लुकअप करते हैं, तो अधिकांश हैश-आधारित तालिकाएँ अपने सबसे अच्छे रूप में होती हैं। उन स्थितियों के लिए जहां आप बहुत अधिक डेटा सम्मिलित करते हैं, फिर परिणाम प्राप्त करने के लिए एक बार तालिका के माध्यम से पुनरावृत्ति करें (उदाहरण के लिए, किसी फ़ाइल में अद्वितीय शब्दों की संख्या की गिनती) संभावना है कि एstd::map बस के रूप में तेजी से होगा, और काफी संभवतः भी तेजी से (लेकिन, फिर से, कम्प्यूटेशनल जटिलता अलग है, ताकि फ़ाइल में अद्वितीय शब्दों की संख्या पर भी निर्भर हो सके)।


1 जहां आप std::less<T>डिफ़ॉल्ट रूप से नक्शा बनाते समय तीसरे टेम्पलेट पैरामीटर द्वारा आदेश को परिभाषित किया जाता है।


1
मुझे लगता है कि मैं जवाब पोस्ट किए जाने के 9 साल बाद आ रहा हूं लेकिन ... क्या आपके पास एक ऐसे डॉक का लिंक है जो इस तथ्य का उल्लेख करता है कि एक अनियंत्रित नक्शा आकार में सिकुड़ सकता है? आमतौर पर, एसटीडी संग्रह केवल बढ़ता है। इसके अलावा, यदि आप बहुत अधिक डेटा डालते हैं, लेकिन पहले से कम या ज्यादा कुंजियों को जानते हैं, तो आप निर्माण पर मानचित्र का आकार निर्दिष्ट कर सकते हैं, जो मूल रूप से आकार परिवर्तन लागत को कम करता है (क्योंकि कोई भी नहीं होगा) ।
ज़ोन्को

@Zonko: क्षमा करें, जब मैंने पूछा तो मैंने इसे नोटिस नहीं किया। जहाँ तक मुझे पता है, एक unordered_map हटना नहीं है, सिवाय कॉल करने के जवाब में rehash। जब आप कॉल करते हैं rehash, तो आप तालिका के लिए एक आकार निर्दिष्ट करते हैं। उस आकार का उपयोग तब तक किया जाएगा जब तक कि ऐसा करने से तालिका के लिए निर्दिष्ट अधिकतम लोड कारक से अधिक न हो (जिस स्थिति में, लोड फैक्टर को सीमा के भीतर रखने के लिए आकार स्वचालित रूप से बढ़ जाएगा)।
जेरी कॉफिन

22

यहाँ एक और अधिक पूर्ण और लचीला उदाहरण है जो आवश्यक नहीं छोड़ता है जिसमें संकलन त्रुटियाँ उत्पन्न करना शामिल है:

#include <iostream>
#include <unordered_map>

class Hashtable {
    std::unordered_map<const void *, const void *> htmap;

public:
    void put(const void *key, const void *value) {
            htmap[key] = value;
    }

    const void *get(const void *key) {
            return htmap[key];
    }

};

int main() {
    Hashtable ht;
    ht.put("Bob", "Dylan");
    int one = 1;
    ht.put("one", &one);
    std::cout << (char *)ht.get("Bob") << "; " << *(int *)ht.get("one");
}

अभी भी कुंजी के लिए विशेष रूप से उपयोगी नहीं है, जब तक कि वे संकेत के रूप में पूर्वनिर्धारित नहीं हैं, क्योंकि एक मिलान मूल्य ऐसा नहीं करेगा! (हालांकि, जब से मैं सामान्य रूप से कुंजी के लिए तार का उपयोग करता हूं, कुंजी की घोषणा में "कॉन्स्ट शून्य" के लिए "स्ट्रिंग" को प्रतिस्थापित करना चाहिए, इस समस्या को हल करना चाहिए।)


4
मुझे कहना होगा, यह उदाहरण C ++ में एक बहुत बुरा अभ्यास है। आप एक दृढ़ता से टाइप की गई भाषा का उपयोग कर रहे हैं और उपयोग करके इसे नष्ट कर रहे हैं void*। शुरुआत के लिए, unordered_mapमानक के हिस्से के रूप में इसे लपेटने का कोई कारण नहीं है और कोड रखरखाव को कम करता है। अगला, यदि इसे लपेटने पर जोर दिया जाए, तो उपयोग करें templates। ठीक यही तो वे हैं।
मर्दाना

जोर से टाइप किया? आप शायद सांख्यिकीय रूप से टाइप किए गए हैं। तथ्य यह है कि वह कास्ट चार पीटीआर से शून्य में जा सकता है चुपचाप सी ++ बनाता है, लेकिन दृढ़ता से, टाइप नहीं। वहाँ प्रकार है, लेकिन संकलक एक बात नहीं कहेंगे जब तक कि आप कुछ अस्पष्ट ध्वज को सक्षम न करें जो कि सबसे अधिक संभावना नहीं है।
साहसाहे

6

साक्ष्य जो std::unordered_mapGCC stdlibc ++ 6.4 में हैश मैप का उपयोग करता है

इस पर उल्लेख किया गया था: https://stackoverflow.com/a/3578247/895245 लेकिन निम्नलिखित उत्तर में: क्या डेटा संरचना std के अंदर है :: C ++ में नक्शा? मैंने आगे GCC stdlibc ++ 6.4 कार्यान्वयन के लिए इस तरह के साक्ष्य दिए हैं:

  • GDB कदम वर्ग में डिबगिंग
  • प्रदर्शन विशेषता विश्लेषण

यहाँ उस उत्तर में वर्णित प्रदर्शन विशेषता ग्राफ का पूर्वावलोकन दिया गया है:

यहां छवि विवरण दर्ज करें

कस्टम क्लास और हैश फ़ंक्शन का उपयोग कैसे करें unordered_map

यह उत्तर देता है कि यह: C ++ बिना किसी कस्टम क्लास प्रकार के कुंजी का उपयोग करता है

अंश: समानता:

struct Key
{
  std::string first;
  std::string second;
  int         third;

  bool operator==(const Key &other) const
  { return (first == other.first
            && second == other.second
            && third == other.third);
  }
};

हैश फंकशन:

namespace std {

  template <>
  struct hash<Key>
  {
    std::size_t operator()(const Key& k) const
    {
      using std::size_t;
      using std::hash;
      using std::string;

      // Compute individual hash values for first,
      // second and third and combine them using XOR
      // and bit shifting:

      return ((hash<string>()(k.first)
               ^ (hash<string>()(k.second) << 1)) >> 1)
               ^ (hash<int>()(k.third) << 1);
    }
  };

}

0

हम में से उन लोगों के लिए यह पता लगाने की कोशिश कर रहे हैं कि मानक टेम्पलेट का उपयोग करते हुए अभी भी हमारी अपनी कक्षाएं कैसे चल रही हैं, एक सरल उपाय है:

  1. अपनी कक्षा में आपको एक समानता ऑपरेटर अधिभार को परिभाषित करने की आवश्यकता होती है ==। यदि आप यह नहीं जानते कि यह कैसे करना है, तो GeeksforGeeks में एक महान ट्यूटोरियल है https://www.geeksforgeeks.org/operator-overloading-c/

  2. मानक नाम स्थान के तहत, अपने वर्गनाम के साथ हैश नामक एक टेम्प्लेट संरचना घोषित करें (नीचे देखें)। मुझे एक महान ब्लॉगपोस्ट मिला जो XOR और बिटशिफ्टिंग का उपयोग करके हैश की गणना करने का एक उदाहरण दिखाता है, लेकिन यह इस प्रश्न के दायरे से बाहर है, लेकिन इसमें हैश कार्यों के साथ-साथ https://prateekvj.com/ 2014/06/05 / का उपयोग कर-हैश समारोह-इन-सी के लिए उपयोगकर्ता परिभाषित वर्गों /

namespace std {

  template<>
  struct hash<my_type> {
    size_t operator()(const my_type& k) {
      // Do your hash function here
      ...
    }
  };

}
  1. तो फिर अपने नए हैश फ़ंक्शन का उपयोग करके एक हैशटेबल को लागू करने के लिए, आपको बस एक std::mapया ऐसा बनाना std::unordered_mapहोगा जैसे आप सामान्य रूप से करते हैं और my_typeकुंजी के रूप में उपयोग करते हैं , मानक पुस्तकालय स्वचालित रूप से हैश फ़ंक्शन को चरण 2 में आपके द्वारा पहले (चरण 2 में) परिभाषित करेगा। आपकी चाभियां।
#include <unordered_map>

int main() {
  std::unordered_map<my_type, other_type> my_map;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.