दो कतारों का उपयोग करके स्टैक को लागू करने का क्या मतलब है?


34

मेरे पास निम्नलिखित होमवर्क प्रश्न हैं:

दो कतारों का उपयोग करके स्टैक विधियों को धक्का (x) और पॉप () लागू करें।

यह मुझे अजीब लगता है क्योंकि:

  • एक ढेर है एक (LIFO) कतार
  • मैं यह नहीं देखता कि इसे लागू करने के लिए आपको दो कतारों की आवश्यकता क्यों होगी

मैंने आसपास खोज की:

और एक दो समाधान मिला। यह वही है जो मैंने समाप्त किया:

public class Stack<T> {
    LinkedList<T> q1 = new LinkedList<T>();
    LinkedList<T> q2 = new LinkedList<T>();

    public void push(T t) {
        q1.addFirst(t);
    }

    public T pop() {
        if (q1.isEmpty()) {
            throw new RuntimeException(
                "Can't pop from an empty stack!");
        }

        while(q1.size() > 1) {
            q2.addFirst( q1.removeLast() );
        }

        T popped = q1.pop();

        LinkedList<T> tempQ = q1;
        q1 = q2;
        q2 = tempQ;

        return popped;
    }
}

लेकिन मुझे समझ में नहीं आ रहा है कि एक कतार का उपयोग करने से क्या फायदा है; दो कतार संस्करण व्यर्थ जटिल लगता है।

मान लें कि हम 2 के अधिक कुशल होने के लिए पुश का चयन करते हैं (जैसा कि मैंने ऊपर किया था), pushवही रहेगा, और popबस अंतिम तत्व के लिए पुनरावृति की आवश्यकता होगी, और इसे वापस करना होगा। दोनों मामलों में, pushहोगा O(1), और popहोगा O(n); लेकिन एकल कतार संस्करण काफी सरल होगा। यह केवल लूप के लिए एकल की आवश्यकता होनी चाहिए।

क्या मैं कुछ भूल रहा हूँ? यहाँ किसी भी जानकारी की सराहना की जाएगी।


17
एक कतार आमतौर पर एक फीफो संरचना का उल्लेख करती है जबकि स्टैक एक एलआईएफओ संरचना है। Java में LinkedList के लिए इंटरफ़ेस एक डैक (डबल एंडेड कतार) है जो FIFO और LIFO दोनों को एक्सेस करने की अनुमति देता है। लिंक्डलिस्ट कार्यान्वयन के बजाय कतार इंटरफ़ेस में प्रोग्रामिंग को बदलने का प्रयास करें ।

12
अधिक सामान्य समस्या दो स्टैक का उपयोग करके एक कतार को लागू करना है। आपको क्रिस ओकासाकी की पुस्तक विशुद्ध रूप से कार्यात्मक डेटा संरचनाओं पर दिलचस्प लग सकती है।
एरिक लिपर्ट

2
एरिक ने जो कहा, उसका निर्माण करते हुए, आप कभी-कभी अपने आप को एक स्टैक आधारित भाषा में पा सकते हैं (जैसे डीसी या दो स्टैक के साथ ऑटोमेटन नीचे धक्का दें) (ट्यूरिंग मशीन के बराबर, क्योंकि, ठीक है, आप और अधिक कर सकते हैं) जहां आप अपने आप को ढूंढ सकते हैं कई ढेर, लेकिन कोई कतार नहीं।

1
@ मिचेल्ट: या आप अपने आप को एक स्टैक पर आधारित सीपीयू
स्लीबेटमैन

11
"एक स्टैक एक (LIFO) कतार है" ... उह, एक कतार एक प्रतीक्षा रेखा है। जैसे पब्लिक टॉयलेट का उपयोग करने के लिए लाइन। क्या आपके द्वारा प्रतीक्षा की जाने वाली लाइनें कभी LIFO फैशन में व्यवहार करती हैं? "LIFO कतार" शब्द का उपयोग करना बंद करें, यह निरर्थक है।
मेहरदाद

जवाबों:


44

कोई फायदा नहीं है: यह एक विशुद्ध शैक्षणिक अभ्यास है।

एक बहुत लंबे समय पहले जब मैं कॉलेज में एक नए था मैं एक ऐसी ही व्यायाम किया था 1 । लक्ष्य छात्रों को forलूप काउंटर के साथ छोरों का उपयोग करके पुनरावृत्त समाधान लिखने के बजाय एल्गोरिदम को लागू करने के लिए ऑब्जेक्ट-ओरिएंटेड प्रोग्रामिंग का उपयोग करना सिखाना था । इसके बजाय, अपने लक्ष्यों को प्राप्त करने के लिए मौजूदा डेटा संरचनाओं को संयोजित और पुन: उपयोग करें।

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


कृपया ध्यान दें कि आपको java.util.Queue का उपयोग करना चाहिए सीधे कार्यान्वयन का उपयोग करने के बजाय अपने कोड में इंटरफ़ेस :

Queue<T> q1 = new LinkedList<T>();
Queue<T> q2 = new LinkedList<T>();

यह आपको अन्य Queueकार्यान्वयन का उपयोग करने की अनुमति देता है , यदि वांछित है, साथ ही साथ 2 तरीके छिपाते हैं जो LinkedListकि Queueइंटरफ़ेस की भावना के आसपास हो सकते हैं । इसमें शामिल है get(int)और pop()(जब आपका कोड संकलित होता है, तो आपके असाइनमेंट की बाधाओं को देखते हुए एक तर्क त्रुटि होती है। अपने चर को घोषित Queueकरने के बजाय LinkedListइसे प्रकट करेंगे)। संबंधित पढ़ना: "एक इंटरफेस के लिए प्रोग्रामिंग" को समझना और इंटरफेस उपयोगी क्यों हैं?

1 मुझे अभी भी याद है: व्यायाम केवल उपयोग करके स्टैक को उल्टा करना था स्टैक इंटरफ़ेस पर तरीकों और java.util.Collectionsअन्य "स्थिर केवल" उपयोगिता वर्गों में कोई उपयोगिता विधियां नहीं थीं । सही समाधान में अस्थायी होल्डिंग ऑब्जेक्ट के रूप में अन्य डेटा संरचनाओं का उपयोग करना शामिल है: आपको विभिन्न डेटा संरचनाओं, उनके गुणों और उन्हें इसे करने के लिए कैसे संयोजित करना है, यह जानना होगा। मेरे अधिकांश CS101 वर्ग को स्टम्प्ड किया, जिन्होंने पहले कभी प्रोग्राम नहीं किया था।

2 विधियाँ अभी भी हैं, लेकिन आप उन्हें बिना जाति या प्रतिबिंब के उपयोग नहीं कर सकते। इसलिए उन गैर-कतार विधियों का उपयोग करना आसान नहीं है


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

19

कोई फायदा नहीं हुआ। आपने सही ढंग से महसूस किया है कि एक स्टैक को लागू करने के लिए क्युस का उपयोग करने से भयानक समय जटिलता होती है। नहीं (सक्षम) प्रोग्रामर कभी "वास्तविक जीवन" में ऐसा कुछ करेगा।

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

  • आप स्टैप्स रिकैप करते हैं
  • आप कतारों को फिर से लगाते हैं
  • आप एल्गोरिथम सोच के आदी हो जाते हैं
  • आप स्टैक-संबंधित एल्गोरिदम सीखते हैं
  • आपको एल्गोरिदम में ट्रेडऑफ़ के बारे में सोचना है
  • क्यू और स्टैक की समानता को साकार करके, आप अपने पाठ्यक्रम के विभिन्न विषयों को जोड़ते हैं
  • आप व्यावहारिक प्रोग्रामिंग अनुभव प्राप्त करते हैं

दरअसल, यह एक बेहतरीन व्यायाम है। मुझे इसे अभी स्वयं करना चाहिए :)


3
@JohnKugelman आपके संपादन के लिए धन्यवाद, लेकिन मेरा वास्तव में "भयानक समय जटिलता" था। एक लिंक्ड-लिस्ट-आधारित स्टैक के लिए push, peekऔर popऑपरेशन ओ (1) में हैं। एक रहने योग्य सरणी-आधारित स्टैक के लिए समान, सिवाय इसके कि pushओ (1) परिशोधन में है, ओ (एन) सबसे खराब स्थिति के साथ। इसकी तुलना में, क्यू-आधारित स्टैक ओ (एन) पुश, ओ (1) पॉप और झांकना, या वैकल्पिक रूप से ओ (1) धक्का, ओ (एन) पॉप और झांकना के साथ काफी हद तक हीन है।
अमन १

1
"भयानक समय जटिलता" और "अत्यधिक हीनता" बिल्कुल सही नहीं है। धक्का और पॉप के लिए परिमित जटिलता अभी भी O (1) है । इसके बारे में TAOCP (vol1?) में एक मजेदार सवाल है (मूल रूप से आपको यह दिखाना होगा कि कोई तत्व जितनी बार स्विच कर सकता है एक स्टैक से दूसरे पर स्थिर होता है)। एक ऑपरेशन के लिए सबसे खराब मामला प्रदर्शन अलग है, लेकिन फिर मैं शायद ही कभी किसी को ओ (एन) के बारे में अर्रेइलिस्ट्स में धक्का देने के बारे में बात कर रहा हूं - आमतौर पर दिलचस्प संख्या नहीं।
वू

5

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

दो कतारों से बने ढेर के लिए? यह एक संदर्भ में समझ में आ सकता है जहां आपके पास बड़ी और तेज कतारें उपलब्ध हैं। यह निश्चित रूप से इस तरह के जावा व्यायाम के रूप में बेकार है। लेकिन यह समझ में आता है अगर वे चैनल या संदेश कतार हैं। (यानी: एन संदेशों को स्थानांतरित करने के लिए, ओ (1) ऑपरेशन के साथ (एन -1) आइटम को एक नई कतार में आगे ले जाने के लिए।)


हम्म .. यह मुझे कंप्यूटिंग के आधार के रूप में शिफ्ट रजिस्टर का उपयोग करने के बारे में और बेल्ट / मिल वास्तुकला के बारे में
सोचने लगा

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

2

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


2

जैसा कि अन्य लोग पहले ही नोट कर चुके हैं: कोई वास्तविक दुनिया का फायदा नहीं है।

वैसे भी, आपके प्रश्न के दूसरे भाग का एक उत्तर, क्यों नहीं एक कतार का उपयोग करना है, जावा से परे है।

जावा में भी Queueइंटरफ़ेस की एक size()विधि है और उस पद्धति के सभी मानक कार्यान्वयन O (1) हैं।

यह जरूरी नहीं है कि सी / सी ++ प्रोग्रामर के रूप में भोले / विहित लिंक्ड सूची के लिए सही है, जो केवल पहले और अंतिम तत्व के लिए संकेत रखेगा और प्रत्येक तत्व अगले तत्व को इंगित करेगा।

उस स्थिति size()में O (n) है और लूप में बचना चाहिए। या कार्यान्वयन अपारदर्शी है और केवल नंगे न्यूनतम add()और प्रदान करता है remove()

इस तरह के कार्यान्वयन से आपको पहले तत्वों की संख्या को दूसरी कतार में स्थानांतरित करके, n-1तत्वों को पहली कतार में स्थानांतरित करना होगा और शेष तत्व को वापस करना होगा।

उस ने कहा, अगर आप जावा-लैंड में रहते हैं तो यह कुछ इस तरह का नहीं होगा।


size()विधि के बारे में अच्छी बात है । हालाँकि, O (1) size()विधि की अनुपस्थिति में , यह स्टैक के लिए अपने वर्तमान आकार का ट्रैक रखने के लिए तुच्छ है। एक-कतार कार्यान्वयन को रोकने के लिए कुछ भी नहीं।
सेमीस्टर

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

0

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

इन चीजों के लिए वास्तविक उपयोग के संदर्भ में, हालांकि, मैं दो के बारे में सोच सकता हूं। इसके लिए एक उपयोग विवश वातावरण में सिस्टम लागू कर रहा है जो इसके लिए डिज़ाइन नहीं किए गए थे : उदाहरण के लिए, Minecraft के redstone ब्लॉक ट्यूरिंग-पूर्ण सिस्टम का प्रतिनिधित्व करने के लिए निकलते हैं, जिसका उपयोग लोग लॉजिक सर्किट और यहां तक ​​कि पूरे सीपीयू को लागू करने के लिए करते हैं। स्क्रिप्ट-चालित गेमिंग के शुरुआती दिनों में, इस तरह से पहले गेम बॉट के कई कार्यान्वयन किए गए थे।

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

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