std :: मानचित्र सम्मिलित करें या std :: मानचित्र खोजें?


90

एक मानचित्र मानकर जहां आप मौजूदा प्रविष्टियों को संरक्षित करना चाहते हैं। 20% समय, आप जो प्रविष्टि डाल रहे हैं वह नया डेटा है। वहाँ एसटीडी :: नक्शा :: खोजने के लिए एक फायदा है तो एसटीडी :: नक्शा :: डालें कि पुनरावृत्त का उपयोग कर डालें? या यह डालने का प्रयास करने के लिए तेज है और फिर इस पर कार्य करता है कि क्या यह इंगित करता है कि रिकॉर्ड रिकॉर्ड था या नहीं डाला गया था?


4
मुझे सही किया गया था और इसका उपयोग करने का इरादा था std :: map :: lower_bound बजाय std :: map :: find।
सुपरपोलॉक

जवाबों:


147

जवाब है आप न तो। इसके बजाय आप स्कॉट Meyers द्वारा प्रभावी STL के आइटम 24 द्वारा सुझाए गए कुछ करना चाहते हैं :

typedef map<int, int> MapType;    // Your map type may vary, just change the typedef

MapType mymap;
// Add elements to map here
int k = 4;   // assume we're searching for keys equal to 4
int v = 0;   // assume we want the value 0 associated with the key of 4

MapType::iterator lb = mymap.lower_bound(k);

if(lb != mymap.end() && !(mymap.key_comp()(k, lb->first)))
{
    // key already exists
    // update lb->second if you care to
}
else
{
    // the key does not exist in the map
    // add it to the map
    mymap.insert(lb, MapType::value_type(k, v));    // Use lb as a hint to insert,
                                                    // so it can avoid another lookup
}

2
यह वास्तव में है कि कैसे काम करता है, चाल यह है कि यह खोज को खोजने और सम्मिलित करने के लिए आवश्यक खोज को जोड़ती है। बेशक, यह सिर्फ डालने का उपयोग करता है और फिर दूसरे रिटर्न मूल्य को देखता है।
puetzk

1
दो प्रश्न: 1) मानचित्र के लिए खोज का उपयोग करने के लिए लोअरबाउंड भिन्न का उपयोग कैसे किया जाता है? 2) 'मैप' के लिए, क्या ऐसा नहीं है कि 'lb = mymap.end ()' के लिए & amp का राइट हैंड हमेशा सही होता है?
रिचर्ड कॉर्डन

11
@ रीचर्ड: खोज () रिटर्न एंड () यदि कुंजी मौजूद नहीं है, तो निचला_बाउंड उस स्थिति को लौटाता है जहां आइटम होना चाहिए (जो बदले में प्रविष्टि संकेत के रूप में इस्तेमाल किया जा सकता है)। @puetzek: मौजूदा कुंजियों के लिए रेफ़रेंस वैल्यू को ओवरराइट करने के लिए "बस सम्मिलित" नहीं किया जाएगा? अगर ओपी की इच्छा है तो यह निश्चित नहीं है।
पेट्रिशन

2
किसी को भी पता है अगर वहाँ unordered_map के लिए समान है?
जियोवानी फंचल

3
@peterchen का नक्शा :: डालें मौजूदा मान को अधिलेखित नहीं करता है यदि यह मौजूद है, तो cplusplus.com/reference/map/map/serser देखें ।
क्रिस ड्रू

11

इस प्रश्न का उत्तर इस बात पर भी निर्भर करता है कि मानचित्र में आपके द्वारा संग्रहित मूल्य प्रकार बनाना कितना महंगा है:

typedef std::map <int, int> MapOfInts;
typedef std::pair <MapOfInts::iterator, bool> IResult;

void foo (MapOfInts & m, int k, int v) {
  IResult ir = m.insert (std::make_pair (k, v));
  if (ir.second) {
    // insertion took place (ie. new entry)
  }
  else if ( replaceEntry ( ir.first->first ) ) {
    ir.second->second = v;
  }
}

एक मान जैसे कि एक इंट के लिए, उपरोक्त एक सम्मिलित द्वारा पीछा करने की तुलना में अधिक कुशल होगा (संकलक अनुकूलन की अनुपस्थिति में)। जैसा कि ऊपर कहा गया है, यह इसलिए है क्योंकि मानचित्र के माध्यम से खोज केवल एक बार होती है।

हालाँकि, कॉल करने के लिए आवश्यक है कि आपके पास पहले से निर्मित नया "मूल्य" हो:

class LargeDataType { /* ... */ };
typedef std::map <int, LargeDataType> MapOfLargeDataType;
typedef std::pair <MapOfLargeDataType::iterator, bool> IResult;

void foo (MapOfLargeDataType & m, int k) {

  // This call is more expensive than a find through the map:
  LargeDataType const & v = VeryExpensiveCall ( /* ... */ );

  IResult ir = m.insert (std::make_pair (k, v));
  if (ir.second) {
    // insertion took place (ie. new entry)
  }
  else if ( replaceEntry ( ir.first->first ) ) {
    ir.second->second = v;
  }
}

'इंसर्ट' को कॉल करने के लिए हम अपने मूल्य प्रकार के निर्माण के लिए महंगी कॉल का भुगतान कर रहे हैं - और इस प्रश्न में आपने जो कहा है उससे आप इस नए मूल्य का 20% समय का उपयोग नहीं करेंगे। उपरोक्त मामले में, यदि मानचित्र मान प्रकार बदलना कोई विकल्प नहीं है, तो पहले यह सुनिश्चित करने के लिए कि हमें तत्व का निर्माण करने की आवश्यकता है, उसे खोजने के लिए अधिक कुशल है।

वैकल्पिक रूप से, मानचित्र के मूल्य प्रकार को आपके पसंदीदा स्मार्ट पॉइंटर प्रकार का उपयोग करके डेटा को हैंडल करने के लिए बदला जा सकता है। सम्मिलित करने के लिए कॉल एक शून्य सूचक (निर्माण के लिए बहुत सस्ता) का उपयोग करता है और केवल यदि आवश्यक हो तो नए डेटा प्रकार का निर्माण किया जाता है।


8

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

तो .. व्यक्तिगत प्राथमिकता के लिए नीचे। मैं हमेशा सम्मिलित करने का प्रयास करता हूं और यदि आवश्यक हो तो अपडेट करता हूं, लेकिन कुछ लोग जोड़ी गई जोड़ी को संभालना पसंद नहीं करते हैं।


5

मुझे लगता है कि यदि आप एक खोज करते हैं तो सम्मिलित करें, अतिरिक्त लागत तब होगी जब आप कुंजी नहीं ढूंढेंगे और सम्मिलित करने के बाद प्रदर्शन करेंगे। यह वर्णमाला के क्रम में पुस्तकों को देखने और पुस्तक को न ढूंढने की तरह है, फिर पुस्तकों के माध्यम से फिर से देखना है कि इसे कहां डालें। यह उबलता है कि आप किस तरह से चाबियों को संभालेंगे और यदि वे लगातार बदल रहे हैं। अब इसमें कुछ लचीलापन है यदि आप इसे नहीं ढूंढते हैं, तो आप लॉग इन कर सकते हैं, अपवाद, जो भी आप चाहते हैं ...


3

मैं शीर्ष उत्तर पर खो गया हूं।

रिटर्न मैप खोजें .end () अगर यह कुछ भी नहीं मिला है जिसका मतलब है कि अगर आप नई चीजें जोड़ रहे हैं तो

iter = map.find();
if (iter == map.end()) {
  map.insert(..) or map[key] = value
} else {
  // do nothing. You said you did not want to effect existing stuff.
}

दो बार के रूप में धीमी है

map.insert

किसी भी तत्व के लिए पहले से ही नक्शे में नहीं है क्योंकि इसे दो बार खोजना होगा। एक बार यह देखने के लिए कि क्या वहाँ है, फिर से नई चीज़ लगाने के लिए जगह ढूंढनी होगी।


1
एसटीएल इन्सर्ट का एक संस्करण एक जोड़ी होता है जिसमें एक इटरेटर और एक बूल होता है। बूल इंगित करता है कि यह मिला या नहीं, इट्रेटर या तो पाया गया प्रविष्टि है या डाला प्रविष्टि है। यह दक्षता के लिए हरा मुश्किल है; असंभव, मैं कहूंगा।
ज़ैन लिंक्स

4
नहीं, चेक किया गया उत्तर उपयोग किया गया lower_bound, नहीं find। नतीजतन, यदि कुंजी नहीं मिली, तो उसने प्रविष्टि बिंदु पर एक पुनरावृत्त लौटाया, अंत नहीं। नतीजतन, यह तेज है।
स्टीवन सुदित

1

यदि आप दक्षता के बारे में चिंतित हैं, तो आप हैश_मैप <> की जांच कर सकते हैं

आमतौर पर नक्शा <> एक द्विआधारी पेड़ के रूप में लागू किया जाता है। आपकी आवश्यकताओं के आधार पर, एक हैश_मैप अधिक कुशल हो सकता है।


से प्यार किया होगा। लेकिन C ++ मानक लाइब्रेरी में कोई hash_map नहीं है, और PHB उस से बाहर कोड की अनुमति नहीं देता है।
सुपरपोलॉक

1
std :: tr1 :: unordered_map हैश मैप है जिसे अगले मानक में जोड़ा जाना प्रस्तावित है, और यह एसटीएल के अधिकांश वर्तमान कार्यान्वयन के भीतर उपलब्ध होना चाहिए।
बेलाज

1

मुझे लगता है कि टिप्पणी छोड़ने के लिए पर्याप्त अंक नहीं हैं, लेकिन टिक जवाब मुझे लंबा लगता है - जब आप इस बात पर विचार करते हैं कि इंट्रैक्टर वैसे भी रिटर्न लौटाता है, तो लोअरबाउंड खोजते हुए क्यों जाएं, जब आप सिर्फ इट्रेटर का उपयोग कर सकते हैं। अजीब।


1
क्योंकि (निश्चित रूप से प्री-सी ++ 11) डालने का मतलब है कि आपको अभी भी एक std::map::value_typeऑब्जेक्ट बनाना है , स्वीकृत उत्तर भी इससे बचता है।
किलियांदस

-1

दक्षता के बारे में कोई भी उत्तर आपके एसटीएल के सटीक कार्यान्वयन पर निर्भर करेगा। सुनिश्चित करने के लिए जानने का एकमात्र तरीका यह है कि दोनों तरीकों को बेंचमार्क किया जाए। मुझे लगता है कि अंतर महत्वपूर्ण होने की संभावना नहीं है, इसलिए आपकी पसंद की शैली के आधार पर निर्णय लें।


1
यह बिल्कुल सच नहीं है। एसटीएल अधिकांश अन्य पुस्तकालयों के विपरीत है जिसमें यह अपने अधिकांश कार्यों के लिए स्पष्ट बिग-ओ आवश्यकताओं को प्रदान करता है। 2 ओ (लॉग एन) और 1 * ओ (लॉग एन) के बीच एक गारंटीकृत अंतर है, इस बात की परवाह किए बिना कि ओ (लॉग एन) व्यवहार को प्राप्त करने के लिए फ़ंक्शन किस कार्यान्वयन का उपयोग करता है। वह अंतर है या नहींआपके मंच पर महत्वपूर्ण यह एक अलग सवाल है। लेकिन फर्क हमेशा रहेगा।
srm

@ ओआरएम को बिग-ओ की आवश्यकताओं को परिभाषित करते हुए अभी भी आपको यह नहीं बताया गया है कि किसी ऑपरेशन को पूर्ण शब्दों में कितना समय लगेगा। आप जिस गारंटी अंतर की बात करते हैं वह मौजूद नहीं है।
मार्क रैनसम

-2

नक्शा [कुंजी] - इसे छाँटने दें। यह आपके इरादे को सबसे प्रभावी ढंग से संप्रेषित कर रहा है।

हाँ, काफी उचित है।

यदि आप एक खोज करते हैं और एक प्रविष्टि करते हैं तो आप 2 x O (लॉग एन) का प्रदर्शन कर रहे होते हैं, जब आपको एक मिस मिलती है, तो केवल यह पता चलता है कि क्या आपको यह सम्मिलित करने की आवश्यकता है कि क्या डालने की आवश्यकता नहीं है । बस एक सीधे सम्मिलित करें और फिर परिणाम की जांच करना वह तरीका है जिससे मैं जाऊंगा।


नहीं, यदि प्रविष्टि मौजूद है, तो यह मौजूदा प्रविष्टि का संदर्भ देता है।
क्रिश कुमलर

2
-1 इस जवाब के लिए। जैसा कि क्रिस के ने कहा, नक्शे का उपयोग करते हुए [कुंजी] = मान मौजूदा प्रविष्टि को अधिलेखित कर देगा, न कि प्रश्न में इसे "संरक्षित" करना। आप नक्शे [कुंजी] का उपयोग करके अस्तित्व के लिए परीक्षण नहीं कर सकते हैं, क्योंकि यह एक डिफ़ॉल्ट निर्मित वस्तु को लौटाएगा यदि कुंजी मौजूद नहीं है, और यह बनाएं कि कुंजी के लिए प्रविष्टि
netjeff

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