2011-01-19 7 views
8

के बीच से एक तत्व निकालें मैं एक अतिरिक्त आवश्यकता के साथ शेड्यूलर के रूप में प्राथमिकता कतार का उपयोग कर रहा हूं। मुझे अनुसूचित वस्तुओं को रद्द करने में सक्षम होना चाहिए। यह प्राथमिकता कतार के बीच से किसी आइटम को हटाने के बराबर है।एक std :: heap

मैं std::priority_queue का उपयोग शीर्ष के अलावा किसी अन्य तत्व तक पहुंच के रूप में नहीं कर सकता।

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

  • std::make_heapओ (3n)
  • std::push_heapओ (एलजी (एन))
  • std::pop_heapहे (2 एलजी (एन))
: सी ++ इन ढेर कार्य प्रदान

मुझे एक नया फ़ंक्शन चाहिए जैसे std::repair_heap एक बड़े-ओ < 3n। मैं इसे छेद के स्थान के साथ प्रदान करूंगा जहां रद्द आइटम का उपयोग किया जाता था और यह ठीक से ढेर को समायोजित करेगा।

यह std::repair_heap फ़ंक्शन प्रदान नहीं करने के लिए एक बड़ी निगरानी प्रतीत होता है। क्या मुझसे साफ़ - साफ़ कुछ चीज़ चूक रही है?

क्या ऐसी लाइब्रेरी है जो एक stl-compliant std::repair_heap प्रदान करती है?

क्या शेड्यूलर मॉडलिंग के लिए कोई बेहतर डेटा संरचना है?

नोट:
मैं कुछ कारणों के लिए एक std::map का उपयोग नहीं कर रहा हूँ।

  • एक ढेर में लगातार स्मृति ओवरहेड है।
  • एक ढेर में शानदार कैश इलाके है।
+0

अगर मैं गलत हूं तो कृपया मुझे सही करें, लेकिन मुझे लगता है कि आपको इसके लिए 'std :: make_heap' कॉल करना होगा, क्योंकि आपको किसी भी तरह के तत्वों को स्थानांतरित करना होगा। –

+1

मैं सिर्फ 'std :: make_heap' का उपयोग कर सकता हूं। लेकिन ऐसा लगता है कि एक तेज विकल्प होना चाहिए। मुझे संदेह है कि 'repair_heap' को _O (lg (n)) _, पुश और पॉप की तरह लिखा जा सकता है। मेरा तर्क है कि 'repair_heap' सिर्फ सिर के बजाय ढेर के बीच से पॉपिंग कर रहा है। –

+0

मैंने इसी तरह की शेड्यूलिंग समस्या (सी ++ में नहीं) के लिए 'repair_heap' जैसी कुछ लिखा है और यह किया जा सकता है। बाद में मैंने 'std :: priority_queue' के साथ एक ही समस्या को मारा और आखिरकार मैंने फैसला किया कि निष्कासन (संभवतः आपके लिए सच नहीं है) की तुलना में निष्कासन इतना कम था कि मैंने तत्व को हटाते समय ढेर की प्रतिलिपि बनाई थी। उस मामले में मेरा लक्ष्य जितना संभव हो उतना नया/अवांछित कोड बनाना था। –

उत्तर

3

दुर्भाग्यवश, मानक इस (काफी महत्वपूर्ण) फ़ंक्शन को याद कर रहा है। जी ++ के साथ, आप ऐसा करने के लिए गैर-मानक फ़ंक्शन std::__adjust_heap का उपयोग कर सकते हैं, लेकिन ऐसा करने का कोई आसान पोर्टेबल तरीका नहीं है - और __adjust_heap g ++ के विभिन्न संस्करणों में थोड़ा अलग है, इसलिए आप इसे g ++ संस्करणों पर पोर्टेबल रूप से भी नहीं कर सकते ।

+0

'std :: __ adjust_heap' सही चीज़ की तरह दिखता है लेकिन कोई दस्तावेज नहीं है। विशेष रूप से मुझे नहीं पता कि '__value' पैरामीटर क्या है। –

+0

http: // stackoverflow।कॉम/प्रश्न/228783/क्या-नियम-के-प्रयोग-के-उपयोग-ए-अंडरस्कोर-इन-ए-सी-पहचानकर्ता सुझाव देते हैं कि "__" जैसे "__" से शुरू होने वाला फ़ंक्शन केवल कार्यान्वयन के लिए है। – gregg

+0

@gregg: केवल ____adjust_heap' _is_ केवल कार्यान्वयन के लिए। यह उपयोग करने के लिए थोड़ा जोखिम भरा बनाता है क्योंकि फ़ंक्शन अनियंत्रित है, हस्ताक्षर बदल सकता है, या पूरी तरह से दूर जा सकता है। खराब स्थिति परिदृश्य यह है कि '__adjust_heap' अर्थशास्त्र बदलता है एक ऐसा तरीका है जो इसके हस्ताक्षर को नहीं बदलता है। उदाहरण के लिए यह ढेर को समायोजित करना बंद कर देता है और अब इसे यादृच्छिक बनाता है। तो मुझे इसका उपयोग करते समय बहुत सावधान रहना होगा और कुछ भयानक यूनिट परीक्षण लिखना होगा। –

0

ऐसा नहीं है कि एक ढेर के बीच से हटाने के पूरे ढेर मतलब हो सकता है मुझे लगता है फिर से बनाया जाना है: कारण कोई repair_heap है है, क्योंकि यह भी ऐसा ही करने (बड़ा, ओह) make_heap के रूप में काम करना होगा ।

क्या आप ढेर में std::pair<bool, Item> डालकर कुछ ऐसा करने में सक्षम हैं और केवल उन्हें हटाने के बजाय आइटम को अमान्य कर सकते हैं? फिर जब वे आखिरकार शीर्ष पर पहुंच जाते हैं तो आइटम को अनदेखा करते हैं और आगे बढ़ते हैं।

+0

अनुसूचित वस्तुओं को अनदेखा करना मेरी मूल रणनीति थी। हालांकि, शेड्यूलर के एक उपयोगकर्ता ने रद्द वस्तुओं के साथ इसे अभिभूत कर दिया। 100 सक्रिय रूप से शेड्यूल आइटम से कम लेकिन रद्द आइटमों के 1000s। इसे हल करने के लिए मैं सही रद्दीकरण जोड़ने की कोशिश कर रहा हूं। –

+2

@deft_code, रद्द किए गए आइटमों की गिनती को बनाए रखने के बारे में और केवल सीमा को पहुंचने पर ढेर का पुनर्निर्माण कैसे करें? –

+3

ढेर के बीच से एक तत्व को हटाने से अधिक महंगा नहीं होता है जो शीर्ष तत्व (ओ (एलजी _एन_)) को हटाता है, इसलिए यह संभव होना चाहिए, यह सिर्फ एसटीएल –

3

आपके repair_heap() कैसे काम करता है? मेरा अनुमान है:

यदि आपका ढेर कुछ इटरेटर रेंज द्वारा परिभाषित किया गया है, तो कहें (हेपबेजिन, हेपएंड)। जिस तत्व को आप निकालना चाहते हैं वह ढेर के कुछ उप-भाग की जड़ है, जिसे कुछ सब्रेंज (सबहेपबिन, सबहेपेंड) द्वारा परिभाषित किया जाता है।std::pop_heap(subHeapBegin, subHeapEnd) का उपयोग करें, फिर subHeapEnd != heapEnd, मानों को *(subHeapEnd-1) और *(heapEnd-1) पर स्वैप करें, जो आपके हटाए गए आइटम को ढेर कंटेनर के अंत में रखना चाहिए। अब आपको अपने उपशीर्षक में * (subHeapEnd-1) पर तत्व को प्रतिबिंबित करना होगा। अगर मुझे कुछ याद नहीं आया है, जो संभव है, तो वह सब कुछ है जो ढेर कंटेनर के अंत तत्व को काटना है।

सही ढंग से कोड करने की कोशिश करने की परेशानी पर जाने से पहले (मैंने कुछ विवरण छोड़ दिए हैं जैसे सबहेपबिन और सबहेपेंड), मैं यह निर्धारित करने के लिए कुछ परीक्षण चलाऊंगा कि make_heap() वास्तव में आपको धीमा कर देता है या नहीं। बिग-ओ उपयोगी है, लेकिन यह वास्तविक निष्पादन समय के समान नहीं है।

0

यहां एक छोटा सा डेल्फी कोड है जिसका उपयोग मैं एक ढेर से वस्तुओं को हटाने के लिए करता था। मैं इस सी ++, जिनमें से आप बोलते हैं और एक की मरम्मत समारोह की जरूरत नहीं है

पहले पॉप पता नहीं है, लेकिन हे .., तो आप बात कैसे काम करता है की एक विचार प्राप्त:

function THeap.Pop: HeapItem; 
begin 
    if fNextIndex > 1 then begin 
    Dec(fNextIndex); 
    Result:= fBuckets[1]; //no zero element 
    fBuckets[1] := fBuckets[fNextIndex]; 
    fBuckets[fNextIndex] := nil; 
    FixHeapDown;   //this has a param defaulting to 
    end 
    else 
    Result:= nil; 
end; 

अब विपरीत, विलोपन:

procedure THeap.Delete(Item: HeapItem); 
var 
    i:integer; 
begin 
    for i:=1 to pred(fNextIndex) do 
    if Item=fBuckets[i] then begin 
     dec(fNextIndex); 
     fBuckets[i] := fBuckets[fNextIndex]; 
     fBuckets[fNextIndex] := nil; 
     FixHeapDown(i); 
     break; 
     end; 
end; 

इसकी निश्चित रूप से नहीं-नहीं भी सोचने के लिए के बारे में कर हम यहाँ क्या कर रहे हैं, लेकिन हे, लागत और नौकरियों कभी कभी बदल सकता हूँ रद्द मिलता है।

आनंद लें। मुझे उम्मीद है कि यह मदद करता है।

3

मुझे लगता है कि आप जानते हैं कि ढेर कंटेनर (इंडेक्स एन) में कौन सा तत्व आप हटाना चाहते हैं।

  1. मूल्य v[n] = BIG; मूल्य BIG ढेर में किसी भी अन्य मूल्यों से वास्तव में बड़ा है निर्धारित करें।
  2. कॉल std::push_heap(v.begin(), v.begin()+n+1);
  3. कॉल std::pop_heap(v.begin(), v.end());
  4. कॉल v.pop_back();
  5. हो गया

ऑपरेशन हे है (ln (एन))

उत्तर: सबूत के लिए अनुरोध

सबसे पहले, एक क्वालीफायर: यह विधि अल के बारे में कुछ मानती है gorithm std push_heap द्वारा उपयोग किया जाता है।
विशेष रूप से, यह मानता है कि std push_heap (v.begin(), v.begin() + n + 1) केवल उन तत्वों के लिए श्रेणी [0, n]
को बदल देगा जो एन के आरोही हैं, यानी, निम्नलिखित सेट में सूचकांक:

http://www.cplusplus.com/reference/algorithm/push_heap/ "एक ढेर रेंज [देखते हुए पहले, पिछले -1), इस समारोह श्रृंखला के लिए एक ढेर माना लागू होता है:

A(n)={n,(n-1)/2,((n-1)/2-1)/2....0}. 

यहाँ एसटीडी push_heap के लिए एक विशिष्ट कल्पना है [पहले, आखिरी) मूल्य को (अंतिम -1) में इसके संबंधित स्थान में रखकर। "

क्या यह पाठ्यपुस्तकों में पढ़ने वाले "सामान्य ढेर एल्गोरिदम" का उपयोग करने की गारंटी देता है? आप मुझे बताओ।

वैसे भी, यह कोड है जिसे आप चला सकते हैं और देख सकते हैं, अनुभवजन्य रूप से, यह काम करता है। मैं कुलपति 2005.

#include <algorithm> 
#include <vector> 
#include <iostream> 

bool is_heap_valid(const std::vector<int> &vin) 
{ 
    std::vector<int> v = vin; 
    std::make_heap(v.begin(), v.end()); 
    return std::equal(vin.begin(), vin.end(), v.begin()); 
} 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    srand(0); 
    std::vector<int> v; 
    for (int i=0; i<100; i++) 
    { 
     v.push_back(rand() % 0x7fff); 
    } 
    std::make_heap(v.begin(), v.end()); 

    bool bfail = false; 
    while(v.size() >= 2) 
    { 
     int n = v.size()/2; 
     v[n] = 0x7fffffff; 
     std::push_heap(v.begin(), v.begin()+n+1); 
     std::pop_heap(v.begin(), v.end()); 
     v.resize(v.size()-1); 
     if (!is_heap_valid(v)) 
     { 
      std::cout << "heap is not valid" << std::endl; 
      bfail = true; 
      break; 
     } 
    } 
    if (!bfail) 
     std::cout << "success" << std::endl; 

    return 0; 

} 

उपयोग कर रहा हूँ लेकिन मैं एक और समस्या है, जो पता है कि कैसे सूचकांक "n" जो हटाना आवश्यक है है। मैं नहीं देख सकता कि std push_heap और std pop_heap का उपयोग करते समय इसका ट्रैक कैसे रखें (ढेर में जगह जानें)। मुझे लगता है कि आपको अपना हीप कोड लिखना होगा और ऑब्जेक्ट को ढेर में स्थानांतरित होने पर ऑब्जेक्ट को ढेर में इंडेक्स लिखना होगा। आह।

+0

मुझे विश्वास नहीं है कि यह काम करेगा। मुझे समझो और मैं इस पर स्वीकृत उत्तर बदल दूंगा। –

+0

मुझे लगता है कि आप वास्तव में std :: push_heap और std :: pop_heap का उपयोग करने में सक्षम होना चाहिए यदि आप ढेर में रख रहे प्रकार के लिए std :: swap को अधिभारित करते हैं। इंडेक्स को ढेर में संग्रहीत करने के लिए अपने ऑब्जेक्ट पर एक सदस्य बनाएं, और इसे ढेर के वर्तमान आकार में सेट करने से पहले डालें। शुरुआत में यह सही मूल्य होगा क्योंकि पहला ढेर डालने का चरण नए तत्व को अंतिम स्तर पर आखिरी वाला बनाना है, इसलिए इसकी अनुक्रमणिका डालने से पहले ढेर का आकार है। फिर हर बार ढेर फ़ंक्शन std :: स्वैप को कॉल करते हैं, तो अपने ओवरराइड को प्रत्येक तत्व पर संग्रहीत इंडेक्स को स्वैप करें। –

+0

दुर्भाग्यवश अनुभवजन्य साक्ष्य अपर्याप्त है। वास्तव में, एक विशेष कार्यान्वयन के लिए भी एक पूर्ण गणितीय सबूत अच्छा नहीं है क्योंकि एक ही कंपाइलर का एक और कंपाइलर या अलग संस्करण एक अलग एल्गोरिदम का उपयोग कर सकता है। समस्या यह है: एकमात्र चीज जो ढेर बनाता है "एक ढेर" पेड़ संरचना द्वारा बनाए रखा गुण है (_it को ** बाइनरी ** हेप_ भी नहीं होना चाहिए)। एक ढेर कार्यान्वयन एक यादृच्छिक अभिगम इटरेटर को _virtual tree_ में बदल देता है। तो जब आप 'शुरूआत' पर पेड़ को पुनर्व्यवस्थित करते हैं ..begin() + n + 1' ढेर गुण अभी भी 'start..end'' पर मान्य हैं? –

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