एक ढेर, दो कतार


59

पृष्ठभूमि

कई साल पहले, जब मैं एक स्नातक था, हमें परिशोधित विश्लेषण पर एक होमवर्क दिया गया था। मैं समस्याओं में से एक को हल करने में असमर्थ था। मैंने इसे comp.theory में पूछा था , लेकिन कोई संतोषजनक परिणाम सामने नहीं आया। मुझे याद है कि टीए ने इस बात पर जोर दिया था कि वह कुछ साबित नहीं कर सकता, और कहा कि वह सबूत भूल गया, और ... [तुम्हें पता है]।

आज, मैंने समस्या को याद किया। मैं अभी भी जानने के लिए उत्सुक था, इसलिए यहाँ यह है ...

प्रश्न

क्या दो कतारों का उपयोग करके एक स्टैक को लागू करना संभव है , ताकि PUSH और POP दोनों परिचालित परिचालित O (1) में चलें ? यदि हाँ, तो आप मुझे बता सकते हैं कैसे?

नोट: स्थिति काफी आसान है यदि हम एक कतार को दो स्टैक के साथ लागू करना चाहते हैं (इसी संचालन ENQUEUE & DEQUEUE के साथ )। कृपया अंतर का निरीक्षण करें।

पुनश्च: उपरोक्त समस्या स्वयं होमवर्क नहीं है। होमवर्क को किसी भी निचले सीमा की आवश्यकता नहीं थी; सिर्फ एक कार्यान्वयन और चल रहे समय विश्लेषण।


2
मेरा अनुमान है कि आप केवल दो कतारों (O (1) या O (log n)) के अलावा सीमित स्थान का उपयोग कर सकते हैं। मेरे लिए असंभव लगता है, क्योंकि हमारे पास लंबे इनपुट स्ट्रीम के आदेश को उलटने का कोई तरीका नहीं है। लेकिन निश्चित रूप से यह कोई सबूत नहीं है जब तक कि इसे कठोर दावे में नहीं बनाया जा सकता…।
त्सुयोशी इतो

@ त्सुयोशी: आप सीमित स्थान धारणा के बारे में सही हैं। और हाँ, यही मैंने (जिद्दी) टीए से कहा था, लेकिन उसने मना कर दिया :(
एमएस डौस्टी

2
@ त्सुयोशी: मुझे नहीं लगता कि आपको सामान्य रूप से अंतरिक्ष पर बाध्य होने की आवश्यकता है, आपको केवल यह मानने की आवश्यकता है कि आपको दो कतारों (और शायद) के अलावा किसी भी स्थान पर ढेर से हटाए गए वस्तुओं को संग्रहीत करने की अनुमति नहीं है। चर की एक निरंतर संख्या)।
केवह

@SadeqDousti मेरी राय में, यह एकमात्र तरीका संभव होगा यदि आप एक कतार से जुड़े-सूची कार्यान्वयन का उपयोग करते थे और कुछ पॉइंटर्स का उपयोग हमेशा "स्टैक" के शीर्ष पर करने के लिए करते थे
चार्ल्स अदीस

2
ऐसा लगता है कि टीए वास्तव में "दो चरणों का उपयोग करके एक कतार को लागू करना" कहना चाह रहा होगा जो वास्तव में "ओ (1) amortized समय" में सटीक रूप से संभव है।
थॉमस अहले

जवाबों:


45

मेरे पास वास्तविक जवाब नहीं है, लेकिन यहाँ कुछ सबूत हैं कि समस्या खुली है:

  • यह मिंग ली, ल्यूक लॉन्गप्र और पॉल एमबी विटैनी, "द पॉवर ऑफ़ द क्यू" में वर्णित नहीं है, स्ट्रक्चर्स 1986, जो कई अन्य निकट से संबंधित सिमुलेशन पर विचार करता है

  • यह मार्टिन हुहैन में उल्लेख नहीं है, "कई कतारों की शक्ति पर", थोर। अनि। विज्ञान। 1993, एक अनुवर्ती कागज।

  • इसका उल्लेख होल्गर पीटरसन, "स्टैक बनाम डेक्स", COCOON 2001 में नहीं किया गया है।

  • बर्टन रोसेनबर्ग, "दो कतारों का उपयोग करते हुए संदर्भ-मुक्त भाषाओं की तेजी से nondeterministic मान्यता", सूचित करें। प्रोक। लेट्ट। 1998, दो कतारों का उपयोग करके किसी भी सीएफएल को पहचानने के लिए एक ओ (एन लॉग एन) दो-कतार एल्गोरिदम देता है। लेकिन एक nondeterministic pushdown automaton रैखिक समय में CFL को पहचान सकता है। इसलिए यदि ओ (लॉग एन) प्रति ऑपरेशन की तुलना में तेजी से दो कतारों के साथ एक स्टैक का अनुकरण किया गया था, तो रोसेनबर्ग और उनके रेफरी को इसके बारे में पता होना चाहिए था।


4
उत्कृष्ट संदर्भ के लिए +1। हालांकि, कुछ तकनीकी चीजें हैं: कुछ कागजात, पहले वाले की तरह, दो कतारों का उपयोग करके एक स्टैक का अनुकरण करने की समस्या पर विचार नहीं करते हैं (जितना मैं सार से कह सकता हूं)। अन्य लोग सबसे खराब स्थिति के विश्लेषण पर विचार करते हैं, न कि परिशोधन लागत।
बजे एमएस डौस्ती

13

नीचे दिया गया उत्तर 'धोखा' है, जबकि इसमें ऑपरेशंस के बीच किसी भी स्थान का उपयोग नहीं किया जाता है, ऑपरेशन स्वयं से अधिक स्पेस का उपयोग कर सकते हैं । इस थ्रेड में कहीं और उत्तर देखें, जिसमें यह समस्या न हो।O(1)

जबकि मेरे पास आपके सटीक प्रश्न का उत्तर नहीं है, मुझे एक एल्गोरिथ्म मिला जो बजाय समय में काम करता है । मेरा मानना ​​है कि यह तंग है, हालांकि मेरे पास कोई सबूत नहीं है। यदि कुछ भी हो, तो एल्गोरिथ्म से पता चलता है कि की निचली सीमा को साबित करने की कोशिश निरर्थक है, इसलिए यह आपके प्रश्न का उत्तर देने में मदद कर सकता है।O(n)O(n)O(n)O(n)O(n)

मैं दो एल्गोरिदम प्रस्तुत करता हूं, पहला पॉप के लिए चलने का समय और दूसरा पॉप के लिए चलने वाले समय के साथ एक सरल एल्गोरिथम है । मैं मुख्य रूप से इसकी सादगी के कारण पहले एक का वर्णन करता हूं ताकि दूसरे को समझना आसान हो।( O(n)O(n)

अधिक विवरण देने के लिए: पहला कोई अतिरिक्त स्थान का उपयोग नहीं करता है, सबसे खराब स्थिति (और परिशोधन) पुश और एक सबसे खराब स्थिति (और परिशोधन) पॉप है, लेकिन सबसे खराब स्थिति व्यवहार हमेशा ट्रिगर नहीं होता है। चूंकि यह दो कतारों से परे किसी अतिरिक्त स्थान का उपयोग नहीं करता है, यह रॉस स्नाइडर द्वारा प्रस्तुत समाधान की तुलना में थोड़ा 'बेहतर' है।O ( n )O(1)O(n)

दूसरा एकल पूर्णांक फ़ील्ड का उपयोग करता है (इसलिए अतिरिक्त स्थान), सबसे खराब स्थिति (और परिशोधित) पुश और amortized पॉप है। यह समय चल रहा है इसलिए 'सरल' दृष्टिकोण की तुलना में काफी बेहतर है, फिर भी यह कुछ अतिरिक्त स्थान का उपयोग करता है।हे ( 1 ) हे ( O(1)O(1)O(n)

पहला एल्गोरिथ्म

हमारी दो कतारें हैं: कतार और कतार । हमारी 'पुश कतार' होगी , जबकि 'स्टैक ऑर्डर' में पहले से मौजूद कतार होगी।एस सी एन डी एफ मैं r रों टी एस सी एन डीfirstsecondfirstsecond

  • पुश को पैरामीटर को सरल रूप से लागू करके किया जाता है ।first
  • पोपिंग निम्नानुसार किया जाता है। यदि खाली है, तो हम बस धोखा देते हैं और परिणाम वापस करते हैं। अन्यथा, हम रिवर्स , के सभी संलग्न को और स्वैप और । हम तब धोखा देते हैं और dequeue के परिणाम को वापस करते हैं।एस सी एन डी एफ मैं r रों टी एस सी एन डी एफ मैं r रों टी मैं r रों टी एस सी एन डी एस सी एन डीfirstsecondfirstsecondfirstfirstsecondsecond

पहली एल्गोरिथ्म के लिए सी # कोड

यह काफी पठनीय होना चाहिए, भले ही आपने पहले कभी C # नहीं देखा हो। यदि आप नहीं जानते हैं कि जेनेरिक क्या हैं, तो स्ट्रिंग्स के ढेर के लिए, अपने दिमाग में 'टी' के सभी उदाहरणों को बदलें।

public class Stack<T> {
    private Queue<T> first = new Queue<T>();
    private Queue<T> second = new Queue<T>();
    public void Push(T value) {
        first.Enqueue(value);
    }
    public T Pop() {
        if (first.Count == 0) {
            if (second.Count > 0)
                return second.Dequeue();
            else
                throw new InvalidOperationException("Empty stack.");
        } else {
            int nrOfItemsInFirst = first.Count;
            T[] reverser = new T[nrOfItemsInFirst];

            // Reverse first
            for (int i = 0; i < nrOfItemsInFirst; i++)
                reverser[i] = first.Dequeue();    
            for (int i = nrOfItemsInFirst - 1; i >= 0; i--)
                first.Enqueue(reverser[i]);

            // Append second to first
            while (second.Count > 0)
                first.Enqueue(second.Dequeue());

            // Swap first and second
            Queue<T> temp = first; first = second; second = temp;

            return second.Dequeue();
        }
    }
}

विश्लेषण

स्पष्ट रूप से पुश समय में काम करता है । पॉप और समय के भीतर सब कुछ छू सकता है , इसलिए हमारे पास सबसे खराब स्थिति में है। एल्गोरिथ्म इस व्यवहार (उदाहरण के लिए) को प्रदर्शित करता है यदि कोई स्टैक पर तत्वों को धकेलता है और फिर बार-बार एक एकल पुश और उत्तराधिकार में एक एकल पॉप ऑपरेशन करता है।O(1)firstsecondO(n)n

दूसरा एल्गोरिथ्म

हमारी दो कतारें हैं: कतार और कतार । हमारी 'पुश कतार' होगी , जबकि 'स्टैक ऑर्डर' में पहले से मौजूद कतार होगी।firstsecondfirstsecond

यह पहली एल्गोरिथ्म, जिसमें हम तुरंत 'फेरबदल' की सामग्री को नहीं है की एक अनुकूलित संस्करण है में । इसके बजाय, यदि की तुलना में तत्वों की एक पर्याप्त रूप से छोटे संख्या में शामिल (अर्थात् में तत्वों की संख्या का वर्गमूल ), हम केवल पुनर्निर्माण ढेर क्रम में और के साथ मर्ज नहीं है ।firstsecondfirstsecondsecondfirstsecond

  • पुश करना अभी भी पैरामीटर पर केवल एनक्यूइंग करके किया जाता है ।first
  • पोपिंग निम्नानुसार किया जाता है। यदि खाली है, तो हम बस धोखा देते हैं और परिणाम वापस करते हैं। अन्यथा, हम की सामग्री को पुनर्गठित कर रहे हैं ताकि वे स्टैक ऑर्डर में हों। यदि हम बस धोखा देते हैं और परिणाम वापस करते हैं। अन्यथा, हम संलग्न पर , स्वैप और विपंक्ति, और परिणाम लौटने।firstsecondfirst|first|<|second|firstsecondfirstfirstsecondsecond

पहली एल्गोरिथ्म के लिए सी # कोड

यह काफी पठनीय होना चाहिए, भले ही आपने पहले कभी C # नहीं देखा हो। यदि आप नहीं जानते हैं कि जेनेरिक क्या हैं, तो स्ट्रिंग्स के ढेर के लिए, अपने दिमाग में 'टी' के सभी उदाहरणों को बदलें।

public class Stack<T> {
    private Queue<T> first = new Queue<T>();
    private Queue<T> second = new Queue<T>();
    int unsortedPart = 0;
    public void Push(T value) {
        unsortedPart++;
        first.Enqueue(value);
    }
    public T Pop() {
        if (first.Count == 0) {
            if (second.Count > 0)
                return second.Dequeue();
            else
                throw new InvalidOperationException("Empty stack.");
        } else {
            int nrOfItemsInFirst = first.Count;
            T[] reverser = new T[nrOfItemsInFirst];

            for (int i = nrOfItemsInFirst - unsortedPart - 1; i >= 0; i--)
                reverser[i] = first.Dequeue();

            for (int i = nrOfItemsInFirst - unsortedPart; i < nrOfItemsInFirst; i++)
                reverser[i] = first.Dequeue();

            for (int i = nrOfItemsInFirst - 1; i >= 0; i--)
                first.Enqueue(reverser[i]);

            unsortedPart = 0;
            if (first.Count * first.Count < second.Count)
                return first.Dequeue();
            else {
                while (second.Count > 0)
                    first.Enqueue(second.Dequeue());

                Queue<T> temp = first; first = second; second = temp;

                return second.Dequeue();
            }
        }
    }
}

विश्लेषण

स्पष्ट रूप से पुश समय में काम करता है ।O(1)

पॉप काम करता है amortized समय में। दो मामले हैं: अगर , तब हम में क्रम में फेरबदल करते हैं समय। यदि , तो हमारे पास पुश के लिए कम से कम कॉल होना चाहिए था । इसलिए, हम केवल इस मामले को पुश और पॉप पर हर कॉल कर सकते हैं। इस मामले का वास्तविक समय चल रहा है , इसलिए परिशोधित समय ।O(n)|first|<|second|firstO(|first|)=O(n)|first||second|nnO(n)O(nn)=O(n)

अंतिम नोट

पॉप को एक ऑपरेशन बनाने की लागत पर अतिरिक्त चर को समाप्त करना संभव है , पॉप करने के बजाय हर कॉल पर पुश करने के बजाय सभी काम करें।O(n)first


मैंने पहले पैराग्राफ को संपादित किया इसलिए मेरा उत्तर प्रश्न के वास्तविक उत्तर के रूप में तैयार किया गया है।
एलेक्स टेन ब्रिंक

6
आप उलटने के लिए एक सरणी (उलटफेर) का उपयोग कर रहे हैं! मुझे नहीं लगता कि आपको ऐसा करने की अनुमति है।
केवह

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

6
"यदि आप सीधे तरीके से दो स्टैक का उपयोग करके एक कतार को लागू करना चाहते हैं, तो आपको एक बिंदु पर एक स्टैक को उल्टा करना होगा, और जहां तक ​​मुझे पता है कि आपको ऐसा करने के लिए अतिरिक्त स्थान की आवश्यकता है" --- आप नहीं करते। एक मेमोरी सेल और दो स्टैक के साथ Enqueue की परिमित लागत को 3 और Dequeue की परिशोधित लागत को 1 (यानी O (1) दोनों) करने का एक तरीका है। कठिन हिस्सा वास्तव में सबूत है, एल्गोरिथ्म का डिज़ाइन नहीं।
एरोन स्टर्लिंग

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

12

मेरे पिछले उत्तर पर कुछ टिप्पणियों के बाद, यह मेरे लिए स्पष्ट हो गया कि मैं कम या ज्यादा धोखा दे रहा था: मैंने अपनी पॉप विधि के निष्पादन के दौरान अतिरिक्त स्थान ( दूसरे एल्गोरिथ्म में अतिरिक्त स्थान) का उपयोग किया।O(n)

निम्न एल्गोरिथ्म पुश और पॉप के निष्पादन के दौरान तरीकों और केवल अतिरिक्त स्थान के बीच किसी अतिरिक्त स्थान का उपयोग नहीं करता है । पुश में एक एमॉर्टाइज़्ड रनिंग टाइम होता है और पॉप में सबसे खराब स्थिति (और एमॉर्टाइज़्ड) रनिंग टाइम होता है।O(1)O(n)O(1)

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

एल्गोरिथ्म

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

  • पुश कतार की शुरुआत में पैरामीटर को 'सम्मिलित' करके किया जाता है: हम पैरामीटर को मापते हैं, और फिर अन्य सभी तत्वों को फिर से जोड़ते हैं । इस तरह, पैरामीटर की शुरुआत में समाप्त होता है ।firstfirstfirst
  • तो का वर्गमूल से भी बड़ा हो जाता है , हम के सभी तत्वों को enqueue पर एक के बाद एक और फिर स्वैप और । इस तरह, (ढेर के शीर्ष) के तत्व के सिर पर समाप्त होते हैं ।firstsecondsecondfirstfirstsecondfirstsecond
  • पॉप dequeueing द्वारा किया जाता है और परिणाम लौटने अगर dequeueing से खाली नहीं है, और नहीं तो और परिणाम लौटने।firstfirstsecond

पहली एल्गोरिथ्म के लिए सी # कोड

यह कोड काफी पठनीय होना चाहिए, भले ही आपने पहले कभी C # नहीं देखा हो। यदि आप नहीं जानते हैं कि जेनेरिक क्या हैं, तो स्ट्रिंग्स के ढेर के लिए, अपने दिमाग में 'टी' के सभी उदाहरणों को बदलें।

public class Stack<T> {
    private Queue<T> first = new Queue<T>();
    private Queue<T> second = new Queue<T>();
    public void Push(T value) {
        // I'll explain what's happening in these comments. Assume we pushed
        // integers onto the stack in increasing order: ie, we pushed 1 first,
        // then 2, then 3 and so on.

        // Suppose our queues look like this:
        // first: in 5 6 out
        // second: in 1 2 3 4 out
        // Note they are both in stack order and first contains the top of
        // the stack.

        // Suppose value == 7:
        first.Enqueue(value);
        // first: in 7 5 6 out
        // second: in 1 2 3 4 out

        // We restore the stack order in first:
        for (int i = 0; i < first.Count - 1; i++)
            first.Enqueue(first.Dequeue());
        // first.Enqueue(first.Dequeue()); is executed twice for this example, the 
        // following happens:
        // first: in 6 7 5 out
        // second: in 1 2 3 4 out
        // first: in 5 6 7 out
        // second: in 1 2 3 4 out

        // first exeeded its capacity, so we merge first and second.
        if (first.Count * first.Count > second.Count) {
            while (second.Count > 0)
                first.Enqueue(second.Dequeue());
            // first: in 4 5 6 7 out
            // second: in 1 2 3 out
            // first: in 3 4 5 6 7 out
            // second: in 1 2 out
            // first: in 2 3 4 5 6 7 out
            // second: in 1 out
            // first: in 1 2 3 4 5 6 7 out
            // second: in out

            Queue<T> temp = first; first = second; second = temp;
            // first: in out
            // second: in 1 2 3 4 5 6 7 out
        }
    }
    public T Pop() {
        if (first.Count == 0) {
            if (second.Count > 0)
                return second.Dequeue();
            else
                throw new InvalidOperationException("Empty stack.");
        } else
            return first.Dequeue();
    }
}

विश्लेषण

स्पष्ट रूप से पॉप समय में सबसे खराब स्थिति में काम करता है ।O(1)

पुश परिशोधन समय में काम करता है । दो मामले हैं: अगर तब पुश समय लेता है । यदि तब पुश समय लेता है , लेकिन इस ऑपरेशन के बाद खाली हो जाएगा। यह ले जाएगा समय से पहले हम इस मामले को फिर से पाने के लिए, तो परिशोधित समय है समय।O(n)|first|<|second|O(n)|first||second|O(n)firstO(n)O(nn)=O(n)


उत्तर हटाने के बारे में, कृपया meta.cstheory.stackexchange.com/q/386/873 पर एक नज़र डालें
एमएस डौस्ती

मैं लाइन नहीं समझ सकता first.Enqueue(first.Dequeue())। क्या आपने कुछ गलत किया है?
बजे एमएस डौस्ती

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

मेरे लिए एल्गोरिथ्म अधिक पठनीय और संपादित करने से पहले समझने में आसान था।
केवह

9

Θ(N)

NNNNN

PUSHN(PUSHNPOPN)N

NN/2

N

N/2N/2

N/22N

N/22NNNN/23NΩ(N)


NN

इन दो मामलों के मिश्रण के बारे में क्या? उदाहरण के लिए, हम धक्का देते हैंnQ1N/2Q22nn4:1+2++n+n2n

जाहिरा तौर पर पीटर का जवाब इस निचली सीमा का खंडन करता है?
जो

@ जो मुझे नहीं लगता कि पीटर का उत्तर इस निचली सीमा के विपरीत है क्योंकि पहले एन पुश इस क्रम में कभी भी पॉप नहीं होते हैं। किसी भी फेरबदल प्रक्रिया के लिए कम से कम O (N) समय की आवश्यकता होगी, इसलिए यदि उसे प्रत्येक `चरण '( अनुक्रम संचालन) करना होगा, तो हम अभी भी चरण के लिए परिशोधन किया है । विशेष रूप से इस तरह के एक एल्गोरिथ्म मेरे विश्लेषण में "पहले मामले" के तहत आता है। PUSHNPOPNO(N)
शॉन हैकर

@hengxin आपकी टिप्पणी ने मुझे एहसास दिलाया कि मैंने अपने तर्क को स्पष्ट रूप से व्यक्त नहीं किया है जैसा कि मुझे पसंद आएगा। मैंने इसे संपादित किया है इसलिए अब यह स्पष्ट होना चाहिए कि आपके द्वारा प्रस्तावित प्रस्ताव को पहले मामले के तहत कवर किया गया है। तर्क यह है कि अगर हम किसी एक तत्व को बड़ी कतार में शामिल करते हैं, तो हमें अंततः इसे पुनः प्राप्त करने के लिए संचालन की आवश्यकता होगी । O(N)
शॉन हैकर

6

p u s hO(lgn)pushpoppopO(lgn)pop अनुरोध किया गया है और आउटपुट कतार खाली है, आप इनपुट कतार को उलटने और आउटपुट पंक्ति में संग्रहीत करने के लिए सही फेरबदल का एक क्रम करते हैं।

O(1)

जहां तक ​​मुझे पता है, यह एक नया विचार है ...



अरे! मुझे एक अद्यतन या संबंधित प्रश्न की तलाश करनी चाहिए थी। आपके द्वारा पूर्व में दिए गए उत्तर के कागजात ने k स्टैक्स और k + 1 स्टैक के बीच संबंध दिखाया। क्या यह ट्रिक k और k + 1 स्टैक के बीच k कतारों की शक्ति को समाप्त करती है? यदि हां, तो यह एक साफ-सुथरी दास्तान है। किसी भी तरह से, मुझे आपके उत्तर से जोड़ने के लिए धन्यवाद, इसलिए मैंने इसे किसी अन्य स्थान के लिए लिखने में बहुत समय बर्बाद नहीं किया।
पीटर बुथे

1

अतिरिक्त स्थान का उपयोग किए बिना, शायद एक प्राथमिकता वाली कतार का उपयोग करते हुए और प्रत्येक नए धक्का को मजबूर करते हुए इसे पिछले वाले की तुलना में बड़ी प्राथमिकता दें? अभी भी O (1) नहीं होगा।


0

मैं amortized निरंतर समय में एक स्टैक को लागू करने के लिए कतारें नहीं प्राप्त कर सकता। हालांकि, मैं सबसे खराब स्थिति रैखिक समय में एक स्टैक को लागू करने के लिए दो कतारें प्राप्त करने का एक तरीका सोच सकता हूं।

  • AB
  • हर बार एक पुश ऑपरेशन होता है, बिट को फ्लिप करें और कतार में तत्व को डालें जो अब सीमांकित हो जाता है। दूसरी कतार से सब कुछ पॉप करें और इसे वर्तमान कतार पर धकेलें।
  • एक पॉप ऑपरेशन वर्तमान कतार के सामने से हट जाता है और बाहरी स्थिति को स्पर्श नहीं करता है।

बेशक, हम एक और बाहरी राज्य जोड़ सकते हैं जो हमें बताता है कि अंतिम ऑपरेशन एक धक्का था या पॉप। जब तक हमें एक पंक्ति में दो पुश ऑपरेशन नहीं मिलते, हम एक कतार से दूसरी पंक्ति में सब कुछ ले जाने में देरी कर सकते हैं। यह पॉप ऑपरेशन को और अधिक जटिल बनाता है। यह हमें ओ (1) पॉप ऑपरेशन के लिए परिमित जटिलता देता है। दुर्भाग्य से धक्का रैखिक रहता है।

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

यदि आप बार-बार परिचालित होने वाले परिचालनों को प्राप्त करना चाहते हैं, तो आपको संभवतः कुछ अधिक चतुर करना होगा।


4
निश्चित रूप से, मैं एक ही बदतर मामले समय जटिलता के साथ और जटिलता के बिना एक कतार का उपयोग कर सकता हूं, अनिवार्य रूप से कतार को एक अतिरिक्त कतार तत्व के साथ एक परिपत्र सूची के रूप में मानकर स्टैक के शीर्ष का प्रतिनिधित्व करता हूं।
डेव क्लार्क

लगता है आप कर सकते हैं! हालांकि, ऐसा लगता है कि इस तरह से स्टैक का अनुकरण करने के लिए एक से अधिक शास्त्रीय कतार आवश्यक है।
रॉस स्नाइडर

0

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

सामने लोडिंग की अनुमति के बिना, यहाँ एक और समाधान है:

इस एल्गोरिथ्म के लिए दो कतारों और दो बिंदुओं की आवश्यकता होती है, हम उन्हें क्रमशः Q1, Q2, प्राथमिक और द्वितीयक कहेंगे। Initization पर Q1 और Q2 खाली होते हैं, प्राथमिक बिंदु Q1 और द्वितीयक बिंदु Q2 तक।

PUSH ऑपरेशन तुच्छ है, इसमें केवल शामिल हैं:

*primary.enqueue(value);

पीओपी ऑपरेशन थोड़ा अधिक शामिल है; इसके लिए प्राथमिक कतार के अंतिम आइटम को माध्यमिक कतार पर स्प्लिंग करने, पॉइंटर्स को स्वैप करने और मूल शेष से अंतिम शेष आइटम को वापस करने की आवश्यकता होती है:

while(*primary.size() > 1)
{
    *secondary.enqueue(*primary.dequeue());
}

swap(primary, secondary);
return(*secondary.dequeue());

कोई सीमा जाँच नहीं की जाती है, और यह O (1) नहीं है।

जैसा कि मैं यह टाइप कर रहा हूं, मैं देखता हूं कि यह एक लूप के स्थान पर लूप के स्थान पर एक लूप का उपयोग करके किया जा सकता है, जैसे एलेक्स ने किया है। किसी भी तरह, PUSH ऑपरेशन O (1) है और POP ऑपरेशन O (n) हो जाता है।


यहां दो कतारों और एक सूचक का उपयोग करते हुए एक और समाधान है, जिसे क्रमशः Q1, Q2, और queue_p कहा जाता है:

आरंभीकरण होने पर, Q1 और Q2 खाली हैं और क्यू के लिए क्यू_पी अंक हैं।

फिर से, PUSH ऑपरेशन तुच्छ है, लेकिन दूसरी कतार में queue_p को इंगित करने के एक अतिरिक्त चरण की आवश्यकता होती है:

*queue_p.enqueue(value);
queue_p = (queue_p == &Q1) ? &Q2 : &Q1;

पीओपी ऑपरेशन ऑपरेशन पहले जैसा है, लेकिन अब n / 2 आइटम हैं जिन्हें कतार के माध्यम से घुमाने की आवश्यकता है:

queue_p = (queue_p == &Q1) ? &Q2 : &Q1;
for(i=0, i<(*queue_p.size()-1, i++)
{
    *queue_p.enqueue(*queue_p.dequeue());
}
return(*queue_p.dequeue());

PUSH ऑपरेशन अभी भी O (1) है, लेकिन अब POP ऑपरेशन O (n / 2) है।

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


एलेक्स को शामिल करने के लिए आपका दूसरा एल्गोरिदम मददगार है।
hengxin

0

kΘ(n1/k)

k
n
O(1)

एक दिशा (यानी ऊपरी बाउंड) में, th कतार का आकार , और निचली-क्रमांकित कतारों के साथ, सबसे हाल का स्टैक प्रति आइटम (सिवाय अगर स्टैक में कम तत्व हैं; भी आइटम केवल स्थानांतरित किए गए हैं और कॉपी नहीं किए गए हैं)। हम इस बाधा को कतारों के बीच स्थानांतरित करके बनाए रखते हैं, बल्क में चालें करते हैं ताकि समय प्रति आइटम पंक्ति में स्थानांतरित हो सके और पंक्ति । प्रत्येक आइटम को उसके स्टैक की पहचान और स्टैक के भीतर उसकी ऊंचाई से एनोटेट किया जाता है; यदि हम पुश को not अनुमति देते हैं (या यदि हमारे पास केवल एक स्टैक है और पॉप-टाइम पॉपुलर करने की अनुमति है) तो इसकी आवश्यकता नहीं है।iΘ(ni/k)Θ(ni/k)O(1)i+1O(n1/k)i1Θ(n1/k)

दूसरी दिशा में (यानी कम बाउंड), हम कुछ तक आइटम जोड़ते रह सकते हैं , वें सबसे हालिया आइटम है, जिसमें से प्रत्येक पंक्ति के अंत से दूर आइटम हैं, और फिर हम इसे अनुरोध करते हैं और दोहराते हैं। मान लीजिए कि यह पर्याप्त नहीं है। फिर, एक नया आइटम आमतौर पर आकार एक कतार में जोड़ा जाना चाहिए । इस कतार के आकार को बनाए रखने के लिए, वस्तुओं को आवृत्ति के साथ दूसरी कतार में ले जाना चाहिए, जिसका आकार आमतौर पर होना चाहिए ताकि कदम के बाद तेजी से पर्याप्त आइटम पुनर्प्राप्ति की अनुमति मिल सके। । इस तर्क को दोहराते हुए, हमें आवश्यकता के अनुसार, (और बढ़ते) के कुल आकार के साथ कतारें मिलती हैं ।मीटर Ω ( मीटर n 1 / कश्मीर ) ( n 1 / कश्मीर ) Ω ( एन 1 / कश्मीर ) ( n 2 / कश्मीर ) कश्मीर ( n )mmΩ(mn1/k)o(n1/k)Ω(n1/k)o(n2/k)ko(n)

इसके अलावा, यदि किसी स्टैक को एक बार में खाली किया जाना चाहिए (इससे पहले कि हम फिर से आइटम जोड़ना शुरू करें), मुझे उम्मीद है कि इष्टतम परिशोधन प्रदर्शन (दो या अधिक कतारों का उपयोग करके एक स्टैक); यह प्रदर्शन (अनिवार्य रूप से) मर्ज सॉर्ट का उपयोग करके प्राप्त किया जा सकता है।Θ(logn)


-3

एक स्टैक को दो कतारों के उपयोग से लागू किया जा सकता है, दूसरी कतार को अब ओफ़र के रूप में उपयोग करके। जब आइटम स्टैक पर धकेल दिए जाते हैं तो उन्हें कतार के अंत में जोड़ा जाता है। जितनी बार कोई आइटम पॉपअप होता है, पहली पंक्ति के n - 1 तत्वों को दूसरे में ले जाया जाना चाहिए, जबकि शेष आइटम वापस आ जाता है। सार्वजनिक वर्ग QueueStack implemen ts IStack {निजी IQueue q1 = new Queue (); निजी IQueue q2 = नई कतार (); सार्वजनिक शून्य पुश (E e) {q1.enqueue (e) // O (1)} सार्वजनिक E पॉप (E e) {जबकि (1 <q1.size ()) // O (n) {q2.enqueue ( q1.dequeue ()); } sw apQueues (); वापसी q2.dequeue (); } p rivate void swapQueues () {IQueue Q = q2; q2 = q1; क्यू 1 = क्यू; }}


2
क्या आप परिशोधित समय O (1) के बारे में प्रश्न में हिस्सा याद करते हैं?
डेविड एपपस्टीन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.