2009-07-08 6 views
83

मेरे पास वर्तमान में std::map<std::string,int> है जो एक अद्वितीय स्ट्रिंग पहचानकर्ता को एक पूर्णांक मान संग्रहीत करता है, और मैं स्ट्रिंग के साथ देखता हूं। यह ज्यादातर जो मैं चाहता हूं, सिवाय इसके कि यह सम्मिलन आदेश का ट्रैक नहीं रखता है। इसलिए जब मैं मान को मुद्रित करने के लिए मानचित्र को फिर से चलाता हूं, तो उन्हें स्ट्रिंग के अनुसार क्रमबद्ध किया जाता है; लेकिन मैं चाहता हूं कि उन्हें (पहले) सम्मिलन के क्रम के अनुसार क्रमबद्ध किया जाए।एक std :: नक्शा जो सम्मिलन के आदेश का ट्रैक रखता है?

मैंने इसके बजाय vector<pair<string,int>> का उपयोग करने के बारे में सोचा, लेकिन मुझे स्ट्रिंग को देखने और 10,000,000 बार पूर्णांक मानों को बढ़ाने की आवश्यकता है, इसलिए मुझे नहीं पता कि एक वेक्टर काफी धीमा होगा या नहीं।

क्या std :: map का उपयोग करने का कोई तरीका है या क्या कोई अन्य स्टडी कंटेनर है जो मेरी ज़रूरत के अनुरूप बेहतर है?

[मैं जीसीसी 3.4 पर हूं, और मेरे पास शायद मेरे std :: map में मूल्यों के 50 से अधिक जोड़े नहीं हैं]।

+3

std :: map के लिए तेज़ लुकअप टाइम का अच्छा हिस्सा इस तथ्य के साथ करना है कि इसे क्रम में क्रमबद्ध किया गया है, इसलिए यह बाइनरी खोज कर सकता है। बस आपका केक नहीं हो सकता है और इसे भी खा सकता है! – bobobobo

+0

फिर आप का उपयोग करके क्या खत्म हो गया? – aggsol

उत्तर

1

आप इसे मानचित्र के साथ नहीं कर सकते हैं, लेकिन आप दो अलग-अलग संरचनाओं का उपयोग कर सकते हैं - नक्शा और वेक्टर और उन्हें सिंक्रनाइज़ रखें - यह तब होता है जब आप मानचित्र से हटाते हैं, वेक्टर से तत्व ढूंढते और हटाते हैं। या आप map<string, pair<int,int>> बना सकते हैं - और अपनी जोड़ी में int के मूल्य के साथ रिकॉर्ड स्थिति में सम्मिलन पर मानचित्र के आकार() को स्टोर किया जाता है, और फिर जब आप प्रिंट करते हैं, तो क्रमबद्ध करने के लिए स्थिति सदस्य का उपयोग करें।

4

यदि आपको दोनों लुकअप रणनीतियों की आवश्यकता है, तो आप दो कंटेनरों के साथ समाप्त हो जाएंगे। आप अपने वास्तविक मूल्यों (int एस) के साथ vector का उपयोग कर सकते हैं, और इसके आगे map< string, vector< T >::difference_type> डाल सकते हैं, इंडेक्स को वेक्टर में वापस कर सकते हैं।

यह सब पूरा करने के लिए, आप एक कक्षा में दोनों को समाहित कर सकते हैं।

लेकिन मुझे कई सूचकांक के साथ boost has a container पर विश्वास है।

49

यदि आपके पास std :: मानचित्र में केवल 50 मान हैं तो आप उन्हें प्रिंट करने से पहले std :: vector पर प्रतिलिपि बना सकते हैं और उचित functor का उपयोग कर std :: sort के माध्यम से सॉर्ट कर सकते हैं।

या आप boost::multi_index का उपयोग कर सकते हैं। यह कई इंडेक्स का उपयोग करने की अनुमति देता है। आपके मामले में वह ऐसा दिखाई दे सकता है:

struct value_t { 
     string s; 
     int i; 
}; 
struct string_tag {}; 
typedef multi_index_container< 
    value_t, 
    indexed_by< 
     random_access<>, // this index represents insertion order 
     hashed_unique< tag<string_tag>, member<value_t, string, &value_t::s> > 
    > 
> values_t; 
+0

बहुत अच्छा है! नौकरी करने के लिए भी एक सदस्य चयनकर्ता है बूस्ट! – xtofl

+1

हां, mult_index बूस्ट में मेरी पसंदीदा विशेषता है :) –

+0

मैंने पहले कभी mult_index का उपयोग नहीं किया है, लेकिन 50-तत्व कंटेनर के लिए IMHO जो थोड़ी अधिक ओवरकिल (बूट करने के लिए बारोक सिंटैक्स के साथ) लगता है। –

17

आप एक std::tr1::unordered_map (हैश तालिका) के साथ एक std::vector गठबंधन हो सकता है। unordered_map के लिए Boost's documentation पर एक लिंक यहां दिया गया है। आप वेक्टर का उपयोग सम्मिलन आदेश और हैश तालिका को लगातार लुकअप करने के लिए ट्रैक रखने के लिए कर सकते हैं। यदि आप सैकड़ों हजारों लुकअप कर रहे हैं, तो std::map और ओ (1) के लिए हैश तालिका के लिए ओ (लॉग एन) लुकअप के बीच का अंतर महत्वपूर्ण हो सकता है।

std::vector<std::string> insertOrder; 
std::tr1::unordered_map<std::string, long> myTable; 

// Initialize the hash table and record insert order. 
myTable["foo"] = 0; 
insertOrder.push_back("foo"); 
myTable["bar"] = 0; 
insertOrder.push_back("bar"); 
myTable["baz"] = 0; 
insertOrder.push_back("baz"); 

/* Increment things in myTable 100000 times */ 

// Print the final results. 
for (int i = 0; i < insertOrder.size(); ++i) 
{ 
    const std::string &s = insertOrder[i]; 
    std::cout << s << ' ' << myTable[s] << '\n'; 
} 
+0

लेकिन निश्चित रूप से, आप काउंटरों को सम्मिलित क्रम से एक्सेस नहीं कर सकते हैं ... – xtofl

+2

@xtofl, यह मेरा उत्तर कैसे उपयोगी नहीं है और इस प्रकार डाउनवोट के योग्य है? क्या मेरा कोड किसी तरह से गलत है? –

+0

यह करने का यह सबसे अच्छा तरीका है। बहुत सस्ती मेमोरी लागत (केवल 50 तारों के लिए!), 'Std :: map' को काम करने की अनुमति देती है जैसा कि यह माना जाता है (यानी आपके द्वारा डालने के अनुसार स्वयं को सॉर्ट करके), और तेज़ रनटाइम है। (मैंने अपना संस्करण लिखने के बाद इसे पढ़ा, जहां मैंने std :: list का उपयोग किया!) – bobobobo

1

यह कुछ हद तक फैसल के जवाब से संबंधित है। आप बस एक मानचित्र और वेक्टर के चारों ओर एक रैपर वर्ग बना सकते हैं और उन्हें आसानी से सिंक्रनाइज़ कर सकते हैं। उचित encapsulation आपको पहुंच विधि को नियंत्रित करने देगा और इसलिए कौन सा कंटेनर उपयोग करने के लिए ... वेक्टर या मानचित्र। यह बूस्ट या ऐसा कुछ भी उपयोग करने से बचाता है।

1

इसे लागू करने का एक और तरीका vector के बजाय map के साथ है। मैं आपको इस दृष्टिकोण को दिखाऊंगा और मतभेदों पर चर्चा करूंगा:

बस एक कक्षा बनाएं जिसमें दृश्यों के पीछे दो नक्शे हैं।

#include <map> 
#include <string> 

using namespace std; 

class SpecialMap { 
    // usual stuff... 

private: 
    int counter_; 
    map<int, string> insertion_order_; 
    map<string, int> data_; 
}; 

फिर आप उचित क्रम में data_ से अधिक iterator के लिए एक इटरेटर को बेनकाब कर सकते हैं। जिस तरह से आप ऐसा कर insertion_order_ के माध्यम से पुनरावृति है, और प्रत्येक तत्व आपको लगता है कि यात्रा से मिलता है, तो आप insertion_order के लिए और अधिक कुशल hash_map उपयोग कर सकते हैं जब से तुम परवाह नहीं है से insertion_order_

मूल्य के साथ data_ में एक देखने करना insertion_order_ के माध्यम से सीधे पुनरावृत्ति के बारे में।

आवेषण करने के लिए, अगर आप इस तरह एक विधि हो सकता है:

void SpecialMap::Insert(const string& key, int value) { 
    // This may be an over simplification... You ought to check 
    // if you are overwriting a value in data_ so that you can update 
    // insertion_order_ accordingly 
    insertion_order_[counter_++] = key; 
    data_[key] = value; 
} 

कई तरीकों से डिजाइन बेहतर बनाने के लिए और प्रदर्शन के बारे में चिंता कर सकते हैं की एक बहुत कुछ कर रहे हैं, लेकिन यह आप आरंभ करने के लिए एक अच्छा कंकाल है इस कार्यक्षमता को अपने आप लागू करने पर। आप इसे टेम्पलेट कर सकते हैं, और आप वास्तव में जोड़े को डेटा_ में मान के रूप में संग्रहीत कर सकते हैं ताकि आप आसानी से प्रविष्टि_ऑर्डर_ में प्रविष्टि का संदर्भ दे सकें। लेकिन मैं इन डिजाइन मुद्दों को एक अभ्यास के रूप में छोड़ देता हूं :-)।

अद्यतन: मैं मैं डेटा में insertion_order_

के लिए मानचित्र बनाम वेक्टर का उपयोग कर की दक्षता के बारे में कुछ
  • लुकअप सीधे कहना चाहिए लगता है, दोनों ही मामलों में हे (1)
  • में आवेषण हैं वेक्टर दृष्टिकोण ओ (1) हैं, नक्शा दृष्टिकोण में आवेषण ओ (लॉगन)
  • वेक्टर दृष्टिकोण में हटाए गए हैं ओ (एन) क्योंकि आपको आइटम को निकालने के लिए स्कैन करना है। मानचित्र दृष्टिकोण के साथ वे ओ (लॉगन) हैं।

शायद यदि आप जितना अधिक उपयोग करने जा रहे हैं, तो आपको वेक्टर दृष्टिकोण का उपयोग करना चाहिए। नक्शा दृष्टिकोण बेहतर होगा यदि आप सम्मिलन आदेश के बजाय एक अलग क्रम (प्राथमिकता की तरह) का समर्थन कर रहे थे।

+0

यदि आप "प्रविष्टि आईडी" द्वारा आइटम प्राप्त करने की आवश्यकता है तो मानचित्र दृष्टिकोण भी बेहतर होता है। उदाहरण के लिए, यदि आप 5 वें स्थान पर गए आइटम को चाहते हैं, तो आप कुंजी 5 (या 4 के साथ insertion_order में एक लुकअप करते हैं, जहां आप counter_ शुरू करते हैं)। वेक्टर दृष्टिकोण के साथ, यदि 5 वां आइटम हटा दिया गया था, तो आपको वास्तव में 6 वां आइटम मिल जाएगा जो डाला गया था। – Tom

0

एक चीज जिस पर आपको विचार करने की आवश्यकता है वह डेटा तत्वों की छोटी संख्या है जिसका आप उपयोग कर रहे हैं। यह संभव है कि वे केवल वेक्टर का उपयोग करने के लिए तेज़ हो जाएंगे। मानचित्र में कुछ ओवरहेड है जो सरल वेक्टर की तुलना में छोटे डेटा सेट में लुकअप करने के लिए अधिक महंगा हो सकता है। इसलिए, यदि आप जानते हैं कि आप हमेशा तत्वों की संख्या के आसपास उपयोग करेंगे, तो कुछ बेंचमार्किंग करें और देखें कि मानचित्र और वेक्टर का प्रदर्शन वह है जो आप वास्तव में सोचते हैं। आपको वेक्टर में लुकअप मिल सकता है जिसमें केवल 50 तत्व नक्शे के समान ही हैं।

9

समांतर list<string> insertionOrder रखें।

जब यह मुद्रित करने के लिए समय है, सूची की पुनरावृति करेंगे और नक्शा में लुकअप है।

each element in insertionOrder // walks in insertionOrder.. 
    print map[ element ].second // but lookup is in map 
1

// इस आदमी की तरह होना चाहिए!

// यह सम्मिलन की जटिलता को बनाए रखता है ओ (लॉगएन) और हटाना भी ओ (लॉगएन) है।आप std::map<std::string,int>; और vector <data>; इस्तेमाल कर सकते हैं जहां नक्शे में आप प्रविष्टि क्रम में वेक्टर और वेक्टर भंडार डेटा में डेटा के स्थान के सूचकांक की दुकान
:

class SpecialMap { 
private: 
    int counter_; 
    map<int, string> insertion_order_; 
    map<string, int> insertion_order_reverse_look_up; // <- for fast delete 
    map<string, Data> data_; 
}; 
1

यहाँ समाधान है कि बढ़ावा के multiindex का उपयोग किए बिना केवल मानक टेम्पलेट लायब्रेरी की आवश्यकता है । यहां डेटा तक पहुंच ओ (लॉग एन) जटिलता है। सम्मिलन आदेश में डेटा प्रदर्शित करने में ओ (एन) जटिलता है। डेटा सम्मिलन में ओ (लॉग एन) जटिलता है।

उदाहरण के लिए:

#include<iostream> 
#include<map> 
#include<vector> 

struct data{ 
int value; 
std::string s; 
} 

typedef std::map<std::string,int> MapIndex;//this map stores the index of data stored 
              //in VectorData mapped to a string    
typedef std::vector<data> VectorData;//stores the data in insertion order 

void display_data_according_insertion_order(VectorData vectorData){ 
    for(std::vector<data>::iterator it=vectorData.begin();it!=vectorData.end();it++){ 
     std::cout<<it->value<<it->s<<std::endl; 
    } 
} 
int lookup_string(std::string s,MapIndex mapIndex){ 
    std::MapIndex::iterator pt=mapIndex.find(s) 
    if (pt!=mapIndex.end())return it->second; 
    else return -1;//it signifies that key does not exist in map 
} 
int insert_value(data d,mapIndex,vectorData){ 
    if(mapIndex.find(d.s)==mapIndex.end()){ 
     mapIndex.insert(std::make_pair(d.s,vectorData.size()));//as the data is to be 
                   //inserted at back 
                   //therefore index is 
                   //size of vector before 
                   //insertion 
     vectorData.push_back(d); 
     return 1; 
    } 
    else return 0;//it signifies that insertion of data is failed due to the presence 
        //string in the map and map stores unique keys 
} 
0

उपयोग boost::multi_index नक्शा और सूची सूचकांक के साथ।

1

जो आप चाहते हैं (बूस्ट का उपयोग किए बिना) मैं एक "आदेशित हैश" कहता हूं, जो अनिवार्य रूप से हैश का मैशप है और स्ट्रिंग या पूर्णांक कुंजी (या दोनों एक ही समय में) के साथ एक लिंक की गई सूची है। एक आदेश दिया गया हैश एक हैश के पूर्ण प्रदर्शन के साथ पुनरावृत्ति के दौरान तत्वों के क्रम को बनाए रखता है।

मैं एक अपेक्षाकृत नई सी ++ स्निपेट लाइब्रेरी को एक साथ रख रहा हूं जो सी ++ लाइब्रेरी डेवलपर्स के लिए सी ++ भाषा में छेद के रूप में दिखाई देता है। यहां जाएं:

https://github.com/cubiclesoft/cross-platform-cpp

ले लो:

templates/detachable_ordered_hash.cpp 
templates/detachable_ordered_hash.h 
templates/detachable_ordered_hash_util.h 

उपयोगकर्ता नियंत्रित डेटा हैश में रखा जाएगा, तो आप भी चाहते हो सकता है:

security/security_csprng.cpp 
security/security_csprng.h 

यह आह्वान:

#include "templates/detachable_ordered_hash.h" 
... 
// The 47 is the nearest prime to a power of two 
// that is close to your data size. 
// 
// If your brain hurts, just use the lookup table 
// in 'detachable_ordered_hash.cpp'. 
// 
// If you don't care about some minimal memory thrashing, 
// just use a value of 3. It'll auto-resize itself. 
int y; 
CubicleSoft::OrderedHash<int> TempHash(47); 
// If you need a secure hash (many hashes are vulnerable 
// to DoS attacks), pass in two randomly selected 64-bit 
// integer keys. Construct with CSPRNG. 
// CubicleSoft::OrderedHash<int> TempHash(47, Key1, Key2); 
CubicleSoft::OrderedHashNode<int> *Node; 
... 
// Push() for string keys takes a pointer to the string, 
// its length, and the value to store. The new node is 
// pushed onto the end of the linked list and wherever it 
// goes in the hash. 
y = 80; 
TempHash.Push("key1", 5, y++); 
TempHash.Push("key22", 6, y++); 
TempHash.Push("key3", 5, y++); 
// Adding an integer key into the same hash just for kicks. 
TempHash.Push(12345, y++); 
... 
// Finding a node and modifying its value. 
Node = TempHash.Find("key1", 5); 
Node->Value = y++; 
... 
Node = TempHash.FirstList(); 
while (Node != NULL) 
{ 
    if (Node->GetStrKey()) printf("%s => %d\n", Node->GetStrKey(), Node->Value); 
    else printf("%d => %d\n", (int)Node->GetIntKey(), Node->Value); 

    Node = Node->NextList(); 
} 

मैं अपने शोध चरण के दौरान इस एसओ थ्रेड में भाग गया ताकि यह देखने के लिए कि ऑर्डर्ड हैश जैसी कुछ भी मुझे पहले से ही बड़ी पुस्तकालय में छोड़ने की आवश्यकता के बिना मौजूद है या नहीं। मैं निराश हो गया था। तो मैंने अपना खुद लिखा। और अब मैंने इसे साझा किया है।

3

टेस्सेल का आदेश दिया गया मानचित्र (और सेट) का एक बहुत अच्छा कार्यान्वयन है जो एमआईटी लाइसेंस है। आप इसे यहाँ पा सकते हैं: ordered-map

मानचित्र उदाहरण

#include <iostream> 
#include <string> 
#include <cstdlib> 
#include "ordered_map.h" 

int main() { 
tsl::ordered_map<char, int> map = {{'d', 1}, {'a', 2}, {'g', 3}}; 
map.insert({'b', 4}); 
map['h'] = 5; 
map['e'] = 6; 

map.erase('a'); 


// {d, 1} {g, 3} {b, 4} {h, 5} {e, 6} 
for(const auto& key_value : map) { 
    std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl; 
} 


map.unordered_erase('b'); 

// Break order: {d, 1} {g, 3} {e, 6} {h, 5} 
for(const auto& key_value : map) { 
    std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl; 
} 
} 
-1

जोड़ी (str, int) और स्थिर पूर्णांक कि डालने पर वृद्धि कर देता है का एक नक्शा कॉल डेटा का अनुक्रमण करता जोड़े। ऐसी संरचना में रखो जो स्थिर इंट वैल को इंडेक्स() सदस्य के साथ वापस कर सकता है?

+1

आपको एक उदाहरण जोड़ना चाहिए। – m02ph3u5