2008-09-18 41 views
80

एक मानचित्र मानना ​​जहां आप मौजूदा प्रविष्टियों को संरक्षित करना चाहते हैं। 20% समय, जो प्रविष्टि आप डालने वाले हैं वह नया डेटा है। क्या std :: map :: करने के लिए कोई फायदा है तो std :: map :: वापस लौटाए गए इटरेटर का उपयोग करके सम्मिलित करें? या क्या यह सम्मिलित करने का प्रयास करने के लिए तेज़ है और फिर इस पर आधारित कार्य करता है कि क्या इटरेटर इंगित करता है कि रिकॉर्ड रिकॉर्ड किया गया था या नहीं डाला गया था?std :: नक्शा डालने या std :: नक्शा ढूंढें?

+4

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

उत्तर

128

उत्तर आप न तो करते हैं। इसके बजाय आप Scott Meyers द्वारा कुछ मद Effective 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()' जब सही होता है && हमेशा सही होता है? –

+8

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

8

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

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

-2

मानचित्र [कुंजी] - इसे हल करने दें। यह आपके इरादे को सबसे प्रभावी ढंग से संचारित कर रहा है।

हाँ, पर्याप्त मेला।

यदि आप कोई खोज करते हैं और फिर एक सम्मिलित करते हैं तो आप 2 एक्स ओ (लॉग एन) कर रहे होते हैं जब आपको याद आती है क्योंकि आपको केवल यह पता करने की सुविधा मिलती है कि आपको डालने की आवश्यकता है या नहीं (निचला_बाउंड वहां आपकी मदद करें)। बस एक सीधी डालने और फिर परिणाम की जांच करना जिस तरह से मैं जाऊंगा।

+0

नहीं, अगर प्रविष्टि मौजूद है, तो यह मौजूदा प्रविष्टि का संदर्भ देता है। इस उत्तर के लिए –

+2

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

+0

कुंजी के लिए प्रविष्टि के रूप में बनाएं, यह बिंदु यह जांचना है कि नक्शा पहले से ही आबादी वाला है और केवल यदि यह वहां नहीं है तो जोड़ें/ओवरराइट करें। मानचित्र का उपयोग [कुंजी] मानता है कि मान हमेशा पहले से ही है। – srm

0

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

+0

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

+0

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

5

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

1

यदि आप दक्षता के बारे में चिंतित हैं, तो आप hash_map<> देख सकते हैं।

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

+0

से प्यार किया होगा। लेकिन सी ++ मानक पुस्तकालय में कोई हैश_मैप नहीं है, और पीएचबी इसके बाहर कोड को अनुमति नहीं देता है। – Superpolock

+0

[std :: tr1 :: unordered_map] (http://en.wikipedia.org/wiki/Technical_Report_1) हैश नक्शा है जिसे अगले मानक में जोड़ा जाने का प्रस्ताव है, और इसके वर्तमान कार्यान्वयन के भीतर उपलब्ध होना चाहिए एसटीएल। – beldaz

10

इस सवाल का जवाब भी कैसे महंगा यह मान प्रकार आप नक्शे में भंडारण कर रहे हैं बनाने के लिए है पर निर्भर करता है:

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% समय का उपयोग नहीं करेंगे। उपरोक्त मामले में, यदि मैप मान प्रकार बदलने एक विकल्प नहीं है तो यह और अधिक कुशल पहले प्रदर्शन करने के लिए 'खोज' की जाँच करने के अगर हम तत्व का निर्माण करने की जरूरत है।

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

2

मैं शीर्ष जवाब पर खो रहा हूँ।

रिटर्न map.end (खोजें) यह कुछ भी करता है, तो आप के रूप में के रूप में धीमी गति से

map.insert 
किसी भी तत्व के लिए

नहीं है तो नई चीजें जोड़ रहे हैं

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

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

+1

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

+0

सहमत हुए। फिर भी "चेक किया गया" जवाब गलत जवाब है। – gman

+4

नहीं, चेक किए गए उत्तर 'lower_bound' का उपयोग किया गया,' ढूंढें 'नहीं। नतीजतन, यदि कुंजी नहीं मिली, तो यह एक पुनरावर्तक को प्रविष्टि बिंदु पर वापस कर दिया, अंत में नहीं। नतीजतन, यह तेज़ है। –

1

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

+1

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

संबंधित मुद्दे