एक std :: नक्शा जो प्रविष्टि के क्रम का ट्रैक रखता है?


113

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

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

वहाँ का उपयोग करने के लिए एक रास्ता है std::mapया वहाँ एक और stdकंटेनर है कि बेहतर मेरी जरूरत के अनुरूप है?

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

धन्यवाद।


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

1
फिर वापस प्रयोग करके आपने क्या किया?
अग्सोल

जवाबों:


56

यदि आपके पास std :: मैप में केवल 50 मान हैं, तो आप उन्हें std पर कॉपी कर सकते हैं :: प्रिंट करने से पहले वेक्टर और एसटीडी के माध्यम से छाँटें :: उपयुक्त फ़ंक्टर का उपयोग करके सॉर्ट करें।

या आप बढ़ावा का उपयोग कर सकते हैं :: 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;

एक दम बढ़िया! बूस्ट भी एक सदस्य चयनकर्ता काम करने के लिए है!
xtofl

2
हाँ, मल्टी_इन्डेक्स मेरी पसंदीदा विशेषता है :)
किरिल वी। ल्याडविंस्की

3
@ क्रिस्तो: यह कंटेनर आकार के बारे में नहीं है, यह इस समस्या के लिए मौजूदा कार्यान्वयन का पुन: उपयोग करने के बारे में है। वह उत्तम दर्जे का है। बेशक, C ++ एक कार्यात्मक भाषा नहीं है, इसलिए वाक्य रचना कुछ विस्तृत है।
xtofl

4
कब से कुंजी स्ट्रोक को बचाने के बारे में प्रोग्रामिंग कर रहा था?
GManNickG

1
इसे पोस्ट करने के लिए धन्यवाद। क्या "डमीज के लिए बूस्ट मल्टी-इंडेक्स" पुस्तक है? मैं इसका इस्तेमाल कर सकता हूं ...
उज्ज्वल

25

आप std::vectorएक std::tr1::unordered_map(एक हैश तालिका) के साथ गठबंधन कर सकते हैं । यहाँ बूस्ट प्रलेखन के लिए एक कड़ी है unordered_map। आप बार-बार देखने के लिए प्रविष्टि क्रम और हैश तालिका का ट्रैक रखने के लिए वेक्टर का उपयोग कर सकते हैं। यदि आप सैकड़ों हज़ारों लुकअप कर रहे हैं, तो std::mapहैश टेबल के लिए O (लॉग एन) लुकअप और O (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';
}

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

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

मुझे लगता है कि std :: वेक्टर या std :: सूची स्वाद का मामला है, और यह स्पष्ट नहीं है कि कौन सा बेहतर है। (वेक्टर में यादृच्छिक अभिगम होता है जिसकी आवश्यकता नहीं होती है, इसमें सन्निहित स्मृति भी होती है, जिसकी आवश्यकता भी नहीं होती है। सूची उन 2 विशेषताओं में से किसी के खर्च के बिना ऑर्डर को संग्रहीत करती है, जैसे बढ़ते समय वास्तविकताओं।
ओलिवर शॉनक्रॉक

14

एक समानांतर रखो list<string> insertionOrder

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

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

1
यह मेरा पहला विचार भी था, लेकिन यह एक 2 कंटेनर में चाबियाँ डुप्लिकेट करता है, है ना? एक std :: string key के मामले में यह शानदार नहीं है, है ना?
ओलिवर शॉनक्रॉक

2
@OliverSchonrock C ++ 17 के रूप में, आप सूची में मौजूद std::string_viewनक्शे की कुंजी का उपयोग कर सकते हैं । यह नकल करने से बचता है लेकिन आपको सावधान रहने की जरूरत है कि तत्व मानचित्र में कीज़ को रेखांकित करते हैं। std::stringinsertionOrderinsertionOrder
फ्लाईएक्‍स

मैंने एक कंटेनर लिखना शुरू किया जो मानचित्र और सूची को एक में एकीकृत करता है: codereview.stackexchange.com/questions/233177/… कोई दोहराव नहीं
ओलिवर शॉनक्रॉक

10

Tessil के पास एक बहुत अच्छा कार्यान्वित मानचित्र है (और सेट) जो MIT लाइसेंस है। आप इसे यहां पा सकते हैं: ऑर्डर-मैप

नक्शा उदाहरण

#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;
}
}

4

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

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

लेकिन मेरा मानना ​​है कि बूस्ट में कई सूचकांकों के साथ एक कंटेनर होता है।


3

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

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

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();
}

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


2

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


2

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

बस एक ऐसा वर्ग बनाएं जिसमें पर्दे के पीछे दो नक्शे हों।

#include <map>
#include <string>

using namespace std;

class SpecialMap {
  // usual stuff...

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

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

hash_mapजब से आप सीधे पुनरावृत्ति के बारे में परवाह नहीं करते हैं तो आप सम्मिलन_ऑर्डर के लिए अधिक कुशल का उपयोग कर सकते हैं 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;
}

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

अपडेट : मुझे लगता है कि मुझे सम्मिलन_क्रम_ के लिए नक्शे बनाम वेक्टर के उपयोग की दक्षता के बारे में कुछ कहना चाहिए

  • डेटा सीधे देखने पर, दोनों मामलों में O (1) हैं
  • सदिश दृष्टिकोण में आवेषण O (1) हैं, नक्शे के दृष्टिकोण में आवेषण O (logn) हैं
  • वेक्टर अप्रोच में डिलीट O (n) हैं क्योंकि आपको आइटम को हटाने के लिए स्कैन करना होगा। नक्शे के दृष्टिकोण के साथ वे ओ (लोगन) हैं।

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


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

2

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

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

#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
}

1

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


1

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


1

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

// यह प्रविष्टि की जटिलता को बनाए रखता है O (logN) और विलोपन भी O (logN) है।

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

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


2
आपको एक उदाहरण जोड़ना चाहिए।
m02ph3u5
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.