it-swarm-vi.tech

Làm thế nào để thực hiện một hàng đợi bằng cách sử dụng hai ngăn xếp?

Giả sử chúng ta có hai ngăn xếp và không có biến tạm thời nào khác.

Có thể "xây dựng" cấu trúc dữ liệu hàng đợi chỉ bằng hai ngăn xếp không?

362
Nitin

Giữ 2 ngăn xếp, hãy gọi chúng là inboxoutbox.

Enqueue :

  • Đẩy phần tử mới vào inbox

Dequeue :

  • Nếu outbox trống, hãy nạp lại bằng cách bật từng phần tử từ inbox và đẩy nó lên outbox

  • Bật và trả lại phần tử hàng đầu từ outbox

Sử dụng phương pháp này, mỗi phần tử sẽ nằm trong mỗi ngăn xếp chính xác một lần - có nghĩa là mỗi phần tử sẽ được đẩy hai lần và bật hai lần, cho các hoạt động thời gian không đổi được khấu hao.

Đây là một triển khai trong Java:

public class Queue<E>
{

    private Stack<E> inbox = new Stack<E>();
    private Stack<E> outbox = new Stack<E>();

    public void queue(E item) {
        inbox.Push(item);
    }

    public E dequeue() {
        if (outbox.isEmpty()) {
            while (!inbox.isEmpty()) {
               outbox.Push(inbox.pop());
            }
        }
        return outbox.pop();
    }

}
665
Dave L.

A - Làm thế nào để đảo ngược một ngăn xếp

Để hiểu cách xây dựng hàng đợi bằng hai ngăn xếp, bạn nên hiểu cách đảo ngược một ngăn xếp tinh thể rõ ràng. Hãy nhớ cách stack hoạt động, nó rất giống với stack stack trên bếp của bạn. Đĩa rửa cuối cùng sẽ nằm trên đỉnh của ngăn xếp sạch, được gọi là L ast I n F irst O ut (LIFO) trong khoa học máy tính.

Hãy tưởng tượng ngăn xếp của chúng tôi như một cái chai như dưới đây;

 enter image description here

Nếu chúng ta đẩy số nguyên tương ứng 1,2,3, thì 3 sẽ nằm trên đỉnh của ngăn xếp. Bởi vì 1 sẽ được đẩy lên trước, sau đó 2 sẽ được đặt lên trên cùng 1. Cuối cùng, 3 sẽ được đặt lên trên cùng của ngăn xếp và trạng thái mới nhất của ngăn xếp của chúng tôi được biểu thị dưới dạng chai sẽ như dưới đây;

 enter image description here

Bây giờ chúng ta có ngăn xếp của chúng ta được biểu diễn dưới dạng một chai được điền với các giá trị 3,2,1. Và chúng tôi muốn đảo ngược ngăn xếp để phần tử trên cùng của ngăn xếp sẽ là 1 và phần tử dưới cùng của ngăn xếp sẽ là 3. Chúng ta có thể làm gì? Chúng ta có thể lấy cái chai và giữ nó lộn ngược để tất cả các giá trị sẽ đảo ngược theo thứ tự?

 enter image description here

Vâng, chúng tôi có thể làm điều đó, nhưng đó là một chai. Để thực hiện cùng một quy trình, chúng ta cần có một ngăn xếp thứ hai sẽ lưu trữ các phần tử ngăn xếp thứ nhất theo thứ tự ngược lại. Hãy đặt ngăn xếp dân cư của chúng tôi sang trái và ngăn xếp trống mới của chúng tôi ở bên phải. Để đảo ngược thứ tự của các phần tử, chúng ta sẽ bật từng phần tử từ ngăn xếp bên trái và Đẩy chúng sang ngăn xếp bên phải. Bạn có thể thấy những gì xảy ra khi chúng tôi làm như vậy trên hình ảnh dưới đây;

 enter image description here

Vì vậy, chúng tôi biết làm thế nào để đảo ngược một ngăn xếp.

B - Sử dụng hai ngăn xếp như một hàng đợi

Ở phần trước, tôi đã giải thích làm thế nào chúng ta có thể đảo ngược thứ tự các phần tử ngăn xếp. Điều này rất quan trọng, bởi vì nếu chúng ta Đẩy và bật các phần tử vào ngăn xếp, đầu ra sẽ chính xác theo thứ tự ngược lại của hàng đợi. Suy nghĩ về một ví dụ, hãy đẩy mảng số nguyên {1, 2, 3, 4, 5} thành một ngăn xếp. Nếu chúng ta bật các phần tử và in chúng cho đến khi ngăn xếp trống, chúng ta sẽ nhận được mảng theo thứ tự ngược lại, đó sẽ là {5, 4, 3, 2, 1} Hãy nhớ rằng với cùng một đầu vào, nếu chúng ta hủy hàng đợi cho đến khi hàng đợi trống, thì đầu ra sẽ là {1, 2, 3, 4, 5}. Vì vậy, rõ ràng là với cùng một thứ tự đầu vào của các phần tử, đầu ra của hàng đợi hoàn toàn ngược lại với đầu ra của ngăn xếp. Vì chúng ta biết cách đảo ngược một ngăn xếp bằng cách sử dụng một ngăn xếp bổ sung, chúng ta có thể xây dựng một hàng đợi bằng hai ngăn xếp.

Mô hình hàng đợi của chúng tôi sẽ bao gồm hai ngăn xếp. Một ngăn xếp sẽ được sử dụng cho hoạt động enqueue (ngăn xếp số 1 ở bên trái, sẽ được gọi là Ngăn xếp đầu vào), một ngăn xếp khác sẽ được sử dụng cho hoạt động dequeue (ngăn xếp số 2 ở bên phải, sẽ được gọi là Ngăn xếp đầu ra). Kiểm tra hình ảnh dưới đây;

 enter image description here

Mã giả của chúng tôi là như dưới đây;


Hoạt động Enqueue

Push every input element to the Input Stack

Hoạt động Dequeue

If ( Output Stack is Empty)
    pop every element in the Input Stack
    and Push them to the Output Stack until Input Stack is Empty

pop from Output Stack

Chúng ta hãy liệt kê các số nguyên tương ứng {1, 2, 3}. Các số nguyên sẽ được đẩy vào Stack đầu vào (Stack # 1) nằm ở bên trái;

 enter image description here

Sau đó, điều gì sẽ xảy ra nếu chúng ta thực hiện một hoạt động dequeue? Bất cứ khi nào một thao tác dequeue được thực thi, hàng đợi sẽ kiểm tra xem Ngăn xếp đầu ra có trống hay không (xem mã giả ở trên) Nếu Ngăn xếp đầu ra trống, thì Ngăn xếp đầu vào sẽ được trích xuất trên đầu ra để các phần tử của Stack đầu vào sẽ được đảo ngược. Trước khi trả về một giá trị, trạng thái của hàng đợi sẽ như dưới đây;

 enter image description here

Kiểm tra thứ tự của các phần tử trong Ngăn xếp đầu ra (Ngăn xếp số 2). Rõ ràng là chúng ta có thể bật các phần tử từ Ngăn xếp đầu ra để đầu ra sẽ giống như khi chúng ta thoát khỏi hàng đợi. Do đó, nếu chúng tôi thực hiện hai thao tác dequeue, đầu tiên chúng tôi sẽ nhận được {1, 2} tương ứng. Sau đó, phần tử 3 sẽ là phần tử duy nhất của Ngăn xếp đầu ra và Ngăn xếp đầu vào sẽ trống. Nếu chúng ta liệt kê các phần tử 4 và 5, thì trạng thái của hàng đợi sẽ như sau;

 enter image description here

Bây giờ Ngăn xếp đầu ra không trống và nếu chúng tôi thực hiện thao tác dequeue, chỉ có 3 sẽ được bật ra từ Ngăn xếp đầu ra. Sau đó, nhà nước sẽ được nhìn thấy như dưới đây;

 enter image description here

Một lần nữa, nếu chúng ta thực hiện thêm hai thao tác dequeue, trên thao tác dequeue đầu tiên, hàng đợi sẽ kiểm tra xem Stack đầu ra có trống không, điều đó có đúng không. Sau đó bật ra các phần tử của Ngăn xếp đầu vào và đẩy chúng vào Ngăn xếp đầu ra cho đến khi Ngăn xếp đầu vào trống, sau đó trạng thái của Hàng đợi sẽ như dưới đây;

 enter image description here

Dễ thấy, đầu ra của hai thao tác dequeue sẽ là {4, 5}

C - Thực hiện xếp hàng được xây dựng với hai ngăn xếp

Đây là một triển khai trong Java. Tôi sẽ không sử dụng triển khai Stack hiện có nên ví dụ ở đây sẽ phát minh lại bánh xe;

C - 1) Lớp MyStack: Thực hiện ngăn xếp đơn giản

public class MyStack<T> {

    // inner generic Node class
    private class Node<T> {
        T data;
        Node<T> next;

        public Node(T data) {
            this.data = data;
        }
    }

    private Node<T> head;
    private int size;

    public void Push(T e) {
        Node<T> newElem = new Node(e);

        if(head == null) {
            head = newElem;
        } else {
            newElem.next = head;
            head = newElem;     // new elem on the top of the stack
        }

        size++;
    }

    public T pop() {
        if(head == null)
            return null;

        T elem = head.data;
        head = head.next;   // top of the stack is head.next

        size--;

        return elem;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public void printStack() {
        System.out.print("Stack: ");

        if(size == 0)
            System.out.print("Empty !");
        else
            for(Node<T> temp = head; temp != null; temp = temp.next)
                System.out.printf("%s ", temp.data);

        System.out.printf("\n");
    }
}

C - 2) Lớp MyQueue: Thực hiện xếp hàng bằng cách sử dụng hai ngăn xếp

public class MyQueue<T> {

    private MyStack<T> inputStack;      // for enqueue
    private MyStack<T> outputStack;     // for dequeue
    private int size;

    public MyQueue() {
        inputStack = new MyStack<>();
        outputStack = new MyStack<>();
    }

    public void enqueue(T e) {
        inputStack.Push(e);
        size++;
    }

    public T dequeue() {
        // fill out all the Input if output stack is empty
        if(outputStack.isEmpty())
            while(!inputStack.isEmpty())
                outputStack.Push(inputStack.pop());

        T temp = null;
        if(!outputStack.isEmpty()) {
            temp = outputStack.pop();
            size--;
        }

        return temp;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

}

C - 3) Mã trình diễn

public class TestMyQueue {

    public static void main(String[] args) {
        MyQueue<Integer> queue = new MyQueue<>();

        // enqueue integers 1..3
        for(int i = 1; i <= 3; i++)
            queue.enqueue(i);

        // execute 2 dequeue operations 
        for(int i = 0; i < 2; i++)
            System.out.println("Dequeued: " + queue.dequeue());

        // enqueue integers 4..5
        for(int i = 4; i <= 5; i++)
            queue.enqueue(i);

        // dequeue the rest
        while(!queue.isEmpty())
            System.out.println("Dequeued: " + queue.dequeue());
    }

}

C - 4) Đầu ra mẫu

Dequeued: 1
Dequeued: 2
Dequeued: 3
Dequeued: 4
Dequeued: 5
185
Levent Divilioglu

Bạn thậm chí có thể mô phỏng một hàng đợi chỉ bằng một ngăn xếp. Ngăn xếp thứ hai (tạm thời) có thể được mô phỏng bằng ngăn xếp cuộc gọi của các cuộc gọi đệ quy đến phương thức chèn. 

Nguyên tắc giữ nguyên khi chèn một phần tử mới vào hàng đợi: 

  • Bạn cần chuyển các phần tử từ ngăn xếp này sang ngăn xếp tạm thời khác, để đảo ngược thứ tự của chúng. 
  • Sau đó, đẩy phần tử mới được chèn vào ngăn xếp tạm thời
  • Sau đó chuyển các phần tử trở lại ngăn xếp ban đầu
  • Phần tử mới sẽ ở dưới cùng của ngăn xếp và phần tử cũ nhất nằm ở trên cùng (đầu tiên được bật lên)

Một lớp Queue chỉ sử dụng một Stack, sẽ như sau:

public class SimulatedQueue<E> {
    private Java.util.Stack<E> stack = new Java.util.Stack<E>();

    public void insert(E elem) {
        if (!stack.empty()) {
            E topElem = stack.pop();
            insert(elem);
            stack.Push(topElem);
        }
        else
            stack.Push(elem);
    }

    public E remove() {
        return stack.pop();
    }
}
79
pythonquick

Sự phức tạp thời gian sẽ tồi tệ hơn, mặc dù. Một thực hiện hàng đợi tốt làm mọi thứ trong thời gian liên tục.

Chỉnh sửa

Không chắc chắn tại sao câu trả lời của tôi đã bị đánh giá thấp ở đây. Nếu chúng tôi lập trình, chúng tôi quan tâm đến sự phức tạp về thời gian và sử dụng hai ngăn xếp tiêu chuẩn để thực hiện một hàng đợi là không hiệu quả. Đó là một điểm rất hợp lệ và có liên quan. Nếu ai đó cảm thấy cần phải hạ thấp điều này nhiều hơn, tôi sẽ quan tâm để biết tại sao.

Chi tiết hơn một chút : về lý do tại sao sử dụng hai ngăn xếp lại tệ hơn chỉ là một hàng đợi: nếu bạn sử dụng hai ngăn xếp và ai đó gọi dequeue trong khi hộp thư đi trống , bạn cần thời gian tuyến tính để đi đến cuối hộp thư đến (như bạn có thể thấy trong mã của Dave).

Bạn có thể triển khai hàng đợi dưới dạng danh sách liên kết đơn (mỗi phần tử trỏ đến phần tử được chèn tiếp theo), giữ một con trỏ bổ sung cho phần tử được chèn cuối cùng để đẩy (hoặc biến nó thành danh sách tuần hoàn). Việc thực hiện hàng đợi và dequeue trên cấu trúc dữ liệu này rất dễ thực hiện trong thời gian liên tục. Đó là trường hợp xấu nhất trong thời gian không đổi, không được khấu hao. Và, như các ý kiến ​​dường như yêu cầu làm rõ điều này, thời gian liên tục trong trường hợp xấu nhất hoàn toàn tốt hơn thời gian không đổi được khấu hao.

11
Tyler

Đặt hàng đợi được thực hiện là q và ngăn xếp được sử dụng để thực hiện q là stack1 và stack2. 

q có thể được thực hiện theo hai cách:

Phương pháp 1 (Bằng cách thực hiện thao tác enQueue tốn kém)

Phương thức này đảm bảo rằng phần tử mới được nhập luôn ở đầu ngăn xếp 1, do đó thao tác deQueue chỉ bật từ stack1. Để đặt phần tử ở đầu stack1, stack2 được sử dụng.

enQueue(q, x)
1) While stack1 is not empty, Push everything from stack1 to stack2.
2) Push x to stack1 (assuming size of stacks is unlimited).
3) Push everything back to stack1.
deQueue(q)
1) If stack1 is empty then error
2) Pop an item from stack1 and return it.

Phương pháp 2 (Bằng cách thực hiện thao tác deQueue tốn kém)

Trong phương thức này, trong hoạt động en-queue, phần tử mới được nhập ở đầu stack1. Trong hoạt động hủy hàng đợi, nếu stack2 trống thì tất cả các phần tử được chuyển sang stack2 và cuối cùng đỉnh của stack2 được trả về.

enQueue(q,  x)
 1) Push x to stack1 (assuming size of stacks is unlimited).

deQueue(q)
 1) If both stacks are empty then error.
 2) If stack2 is empty
   While stack1 is not empty, Push everything from stack1 to stack2.
 3) Pop the element from stack2 and return it.

Phương pháp 2 chắc chắn tốt hơn phương pháp 1. Phương pháp 1 di chuyển tất cả các phần tử hai lần trong hoạt động enQueue, trong khi phương thức 2 (trong hoạt động deQueue) di chuyển các phần tử một lần và chỉ di chuyển các phần tử nếu stack2 trống.

7
Rahul Gandhi

Một giải pháp trong c #

 public class Queue<T> where T : class
    {
        private Stack<T> input = new Stack<T>();
        private Stack<T> output = new Stack<T>();
        public void Enqueue(T t)
        {
            input.Push(t);
        }

        public T Dequeue()
        {
            if (output.Count == 0)
            {
                while (input.Count != 0)
                {
                    output.Push(input.Pop());
                }
            }
            return output.Pop();
        }
}
3
Santhosh

Bạn sẽ phải bật mọi thứ ra khỏi ngăn xếp đầu tiên để có được phần tử dưới cùng. Sau đó đặt tất cả chúng trở lại vào ngăn xếp thứ hai cho mọi hoạt động "dequeue".

2
user11055

cho nhà phát triển c # ở đây là chương trình hoàn chỉnh:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QueueImplimentationUsingStack
{
    class Program
    {
        public class Stack<T>
        {
            public int size;
            public Node<T> head;
            public void Push(T data)
            {
                Node<T> node = new Node<T>();
                node.data = data;
                if (head == null)
                    head = node;
                else
                {
                    node.link = head;
                    head = node;
                }
                size++;
                Display();
            }
            public Node<T> Pop()
            {
                if (head == null)
                    return null;
                else
                {
                    Node<T> temp = head;
                    //temp.link = null;
                    head = head.link;
                    size--;
                    Display();
                    return temp;
                }
            }
            public void Display()
            {
                if (size == 0)
                    Console.WriteLine("Empty");
                else
                {
                    Console.Clear();
                    Node<T> temp = head;
                    while (temp!= null)
                    {
                        Console.WriteLine(temp.data);
                        temp = temp.link;
                    }
                }
            }
        }

        public class Queue<T>
        {
            public int size;
            public Stack<T> inbox;
            public Stack<T> outbox;
            public Queue()
            {
                inbox = new Stack<T>();
                outbox = new Stack<T>();
            }
            public void EnQueue(T data)
            {
                inbox.Push(data);
                size++;
            }
            public Node<T> DeQueue()
            {
                if (outbox.size == 0)
                {
                    while (inbox.size != 0)
                    {
                        outbox.Push(inbox.Pop().data);
                    }
                }
                Node<T> temp = new Node<T>();
                if (outbox.size != 0)
                {
                    temp = outbox.Pop();
                    size--;
                }
                return temp;
            }

        }
        public class Node<T>
        {
            public T data;
            public Node<T> link;
        }

        static void Main(string[] args)
        {
            Queue<int> q = new Queue<int>();
            for (int i = 1; i <= 3; i++)
                q.EnQueue(i);
           // q.Display();
            for (int i = 1; i < 3; i++)
                q.DeQueue();
            //q.Display();
            Console.ReadKey();
        }
    }
}
2
Jaydeep Shil

Hai ngăn xếp trong hàng đợi được định nghĩa là ngăn xếp1 và ngăn xếp2.

Enqueue: Các yếu tố xuất tinh luôn bị đẩy vào ngăn xếp1

Dequeue: Đỉnh của ngăn xếp2 có thể bật ra vì đây là phần tử đầu tiên được chèn vào hàng đợi khi ngăn xếp2 không có sản phẩm nào. Khi nào ngăn xếp2 trống rỗng, chúng tôi bật tất cả các yếu tố từ ngăn xếp1 và đẩy chúng vào ngăn xếp2 từng cái một. Phần tử đầu tiên trong hàng đợi được đẩy xuống dưới cùng của ngăn xếp1. Nó có thể được bật ra trực tiếp sau khi bật và đẩy các hoạt động vì nó nằm trên đỉnh của ngăn xếp2.

Dưới đây là mã mẫu C++ giống nhau:

template <typename T> class CQueue
{
public:
    CQueue(void);
    ~CQueue(void);

    void appendTail(const T& node); 
    T deleteHead();                 

private:
    stack<T> stack1;
    stack<T> stack2;
};

template<typename T> void CQueue<T>::appendTail(const T& element) {
    stack1.Push(element);
} 

template<typename T> T CQueue<T>::deleteHead() {
    if(stack2.size()<= 0) {
        while(stack1.size()>0) {
            T& data = stack1.top();
            stack1.pop();
            stack2.Push(data);
        }
    }


    if(stack2.size() == 0)
        throw new exception("queue is empty");


    T head = stack2.top();
    stack2.pop();


    return head;
}

Giải pháp này được mượn từ blog của tôi . Phân tích chi tiết hơn với các mô phỏng hoạt động từng bước có sẵn trong trang web blog của tôi.

2
Harry He
// Two stacks s1 Original and s2 as Temp one
    private Stack<Integer> s1 = new Stack<Integer>();
    private Stack<Integer> s2 = new Stack<Integer>();

    /*
     * Here we insert the data into the stack and if data all ready exist on
     * stack than we copy the entire stack s1 to s2 recursively and Push the new
     * element data onto s1 and than again recursively call the s2 to pop on s1.
     * 
     * Note here we can use either way ie We can keep pushing on s1 and than
     * while popping we can remove the first element from s2 by copying
     * recursively the data and removing the first index element.
     */
    public void insert( int data )
    {
        if( s1.size() == 0 )
        {
            s1.Push( data );
        }
        else
        {
            while( !s1.isEmpty() )
            {
                s2.Push( s1.pop() );
            }
            s1.Push( data );
            while( !s2.isEmpty() )
            {
                s1.Push( s2.pop() );
            }
        }
    }

    public void remove()
    {
        if( s1.isEmpty() )
        {
            System.out.println( "Empty" );
        }
        else
        {
            s1.pop();

        }
    }
1
imvp

Việc triển khai hàng đợi bằng hai ngăn xếp trong Swift:

struct Stack<Element> {
    var items = [Element]()

    var count : Int {
        return items.count
    }

    mutating func Push(_ item: Element) {
        items.append(item)
    }

    mutating func pop() -> Element? {
        return items.removeLast()
    }

    func peek() -> Element? {
        return items.last
    }
}

struct Queue<Element> {
    var inStack = Stack<Element>()
    var outStack = Stack<Element>()

    mutating func enqueue(_ item: Element) {
        inStack.Push(item)
    }

    mutating func dequeue() -> Element? {
        fillOutStack() 
        return outStack.pop()
    }

    mutating func peek() -> Element? {
        fillOutStack()
        return outStack.peek()
    }

    private mutating func fillOutStack() {
        if outStack.count == 0 {
            while inStack.count != 0 {
                outStack.Push(inStack.pop()!)
            }
        }
    }
}
1
davejlin

Trong khi bạn sẽ nhận được rất nhiều bài viết liên quan đến việc thực hiện một hàng đợi với hai ngăn xếp: 1. Hoặc bằng cách làm cho quá trình enQueue tốn kém hơn rất nhiều 2. Hoặc bằng cách làm cho quá trình deQueue tốn kém hơn rất nhiều

https://www.geekforgeek.org/queue-USE-stacks/

Một cách quan trọng tôi đã tìm ra từ bài viết trên là xây dựng hàng đợi chỉ với cấu trúc dữ liệu ngăn xếp và ngăn xếp cuộc gọi đệ quy.

Mặc dù người ta có thể lập luận rằng theo nghĩa đen, điều này vẫn đang sử dụng hai ngăn xếp, nhưng lý tưởng nhất là đây chỉ sử dụng một cấu trúc dữ liệu ngăn xếp.

Dưới đây là giải thích của vấn đề:

  1. Khai báo một ngăn xếp đơn để enQueue và khử dữ liệu và đẩy dữ liệu vào ngăn xếp.

  2. trong khi deQueueing có một điều kiện cơ bản trong đó phần tử của ngăn xếp được bật lên khi kích thước của ngăn xếp là 1. Điều này sẽ đảm bảo rằng không có tràn ngăn xếp trong quá trình đệ quy deQueue.

  3. Trong khi deQueueing đầu tiên bật dữ liệu từ đầu ngăn xếp. Lý tưởng nhất là phần tử này sẽ là phần tử có mặt ở đầu ngăn xếp. Bây giờ một khi điều này được thực hiện, hãy gọi đệ quy hàm deQueue và sau đó Đẩy phần tử bật ở trên trở lại vào ngăn xếp.

Mã sẽ trông như dưới đây:

if (s1.isEmpty())
System.out.println("The Queue is empty");
        else if (s1.size() == 1)
            return s1.pop();
        else {
            int x = s1.pop();
            int result = deQueue();
            s1.Push(x);
            return result;

Bằng cách này, bạn có thể tạo một hàng đợi bằng cách sử dụng cấu trúc dữ liệu ngăn xếp đơn và ngăn xếp cuộc gọi đệ quy.

1
Radioactive

đây là giải pháp của tôi trong Java bằng cách sử dụng danh sách liên kết.

class queue<T>{
static class Node<T>{
    private T data;
    private Node<T> next;
    Node(T data){
        this.data = data;
        next = null;
    }
}
Node firstTop;
Node secondTop;

void Push(T data){
    Node temp = new Node(data);
    temp.next = firstTop;
    firstTop = temp;
}

void pop(){
    if(firstTop == null){
        return;
    }
    Node temp = firstTop;
    while(temp != null){
        Node temp1 = new Node(temp.data);
        temp1.next = secondTop;
        secondTop = temp1;
        temp = temp.next;
    }
    secondTop = secondTop.next;
    firstTop = null;
    while(secondTop != null){
        Node temp3 = new Node(secondTop.data);
        temp3.next = firstTop;
        firstTop = temp3;
        secondTop = secondTop.next;
    }
}

}

Lưu ý: Trong trường hợp này, thao tác pop rất tốn thời gian. Vì vậy, tôi sẽ không đề xuất tạo một hàng đợi bằng hai ngăn xếp. 

0
Irshad ck

Tôi sẽ trả lời câu hỏi này trong Go vì Go không có nhiều bộ sưu tập phong phú trong thư viện tiêu chuẩn của nó.

Vì một ngăn xếp thực sự dễ thực hiện, tôi nghĩ tôi nên thử và sử dụng hai ngăn xếp để thực hiện một hàng đợi kết thúc kép. Để hiểu rõ hơn về cách tôi đi đến câu trả lời của mình, tôi đã chia phần thực hiện thành hai phần, phần đầu hy vọng sẽ dễ hiểu hơn nhưng chưa hoàn chỉnh.

type IntQueue struct {
    front       []int
    back        []int
}

func (q *IntQueue) PushFront(v int) {
    q.front = append(q.front, v)
}

func (q *IntQueue) Front() int {
    if len(q.front) > 0 {
        return q.front[len(q.front)-1]
    } else {
        return q.back[0]
    }
}

func (q *IntQueue) PopFront() {
    if len(q.front) > 0 {
        q.front = q.front[:len(q.front)-1]
    } else {
        q.back = q.back[1:]
    }
}

func (q *IntQueue) PushBack(v int) {
    q.back = append(q.back, v)
}

func (q *IntQueue) Back() int {
    if len(q.back) > 0 {
        return q.back[len(q.back)-1]
    } else {
        return q.front[0]
    }
}

func (q *IntQueue) PopBack() {
    if len(q.back) > 0 {
        q.back = q.back[:len(q.back)-1]
    } else {
        q.front = q.front[1:]
    }
}

Về cơ bản, đó là hai ngăn xếp mà chúng ta cho phép phần dưới của các ngăn xếp được thao tác với nhau. Tôi cũng đã sử dụng các quy ước đặt tên STL, trong đó các thao tác Push, pop, peek truyền thống của ngăn xếp có tiền tố trước/sau cho dù chúng tham chiếu đến mặt trước hay mặt sau của hàng đợi.

Vấn đề với đoạn mã trên là nó không sử dụng bộ nhớ rất hiệu quả. Trên thực tế, nó phát triển vô tận cho đến khi bạn hết không gian. Nó rất là tệ. Cách khắc phục cho việc này chỉ đơn giản là sử dụng lại phần dưới cùng của không gian ngăn xếp bất cứ khi nào có thể. Chúng tôi phải giới thiệu một phần bù để theo dõi điều này vì một lát cắt trong Go không thể phát triển ở phía trước một khi được thu nhỏ.

type IntQueue struct {
    front       []int
    frontOffset int
    back        []int
    backOffset  int
}

func (q *IntQueue) PushFront(v int) {
    if q.backOffset > 0 {
        i := q.backOffset - 1
        q.back[i] = v
        q.backOffset = i
    } else {
        q.front = append(q.front, v)
    }
}

func (q *IntQueue) Front() int {
    if len(q.front) > 0 {
        return q.front[len(q.front)-1]
    } else {
        return q.back[q.backOffset]
    }
}

func (q *IntQueue) PopFront() {
    if len(q.front) > 0 {
        q.front = q.front[:len(q.front)-1]
    } else {
        if len(q.back) > 0 {
            q.backOffset++
        } else {
            panic("Cannot pop front of empty queue.")
        }
    }
}

func (q *IntQueue) PushBack(v int) {
    if q.frontOffset > 0 {
        i := q.frontOffset - 1
        q.front[i] = v
        q.frontOffset = i
    } else {
        q.back = append(q.back, v)
    }
}

func (q *IntQueue) Back() int {
    if len(q.back) > 0 {
        return q.back[len(q.back)-1]
    } else {
        return q.front[q.frontOffset]
    }
}

func (q *IntQueue) PopBack() {
    if len(q.back) > 0 {
        q.back = q.back[:len(q.back)-1]
    } else {
        if len(q.front) > 0 {
            q.frontOffset++
        } else {
            panic("Cannot pop back of empty queue.")
        }
    }
}

Đó là rất nhiều chức năng nhỏ nhưng trong số 6 chức năng thì 3 chức năng này chỉ là gương của người khác.

0
John Leidegren

Dưới đây là giải pháp trong ngôn ngữ javascript sử dụng cú pháp ES6.

Stack.js

//stack using array
class Stack {
  constructor() {
    this.data = [];
  }

  Push(data) {
    this.data.Push(data);
  }

  pop() {
    return this.data.pop();
  }

  peek() {
    return this.data[this.data.length - 1];
  }

  size(){
    return this.data.length;
  }
}

export { Stack };

QueueUsingTwoStacks.js

import { Stack } from "./Stack";

class QueueUsingTwoStacks {
  constructor() {
    this.stack1 = new Stack();
    this.stack2 = new Stack();
  }

  enqueue(data) {
    this.stack1.Push(data);
  }

  dequeue() {
    //if both stacks are empty, return undefined
    if (this.stack1.size() === 0 && this.stack2.size() === 0)
      return undefined;

    //if stack2 is empty, pop all elements from stack1 to stack2 till stack1 is empty
    if (this.stack2.size() === 0) {
      while (this.stack1.size() !== 0) {
        this.stack2.Push(this.stack1.pop());
      }
    }

    //pop and return the element from stack 2
    return this.stack2.pop();
  }
}

export { QueueUsingTwoStacks };

Dưới đây là cách sử dụng:

index.js

import { StackUsingTwoQueues } from './StackUsingTwoQueues';

let que = new QueueUsingTwoStacks();
que.enqueue("A");
que.enqueue("B");
que.enqueue("C");

console.log(que.dequeue());  //output: "A"
0
Jyoti Prasad Pal
public class QueueUsingStacks<T>
{
    private LinkedListStack<T> stack1;
    private LinkedListStack<T> stack2;

    public QueueUsingStacks()
    {
        stack1=new LinkedListStack<T>();
        stack2 = new LinkedListStack<T>();

    }
    public void Copy(LinkedListStack<T> source,LinkedListStack<T> dest )
    {
        while(source.Head!=null)
        {
            dest.Push(source.Head.Data);
            source.Head = source.Head.Next;
        }
    }
    public void Enqueue(T entry)
    {

       stack1.Push(entry);
    }
    public T Dequeue()
    {
        T obj;
        if (stack2 != null)
        {
            Copy(stack1, stack2);
             obj = stack2.Pop();
            Copy(stack2, stack1);
        }
        else
        {
            throw new Exception("Stack is empty");
        }
        return obj;
    }

    public void Display()
    {
        stack1.Display();
    }


}

Đối với mọi hoạt động enqueue, chúng tôi thêm vào đầu stack1. Đối với mỗi dequeue, chúng tôi làm trống nội dung của stack1 vào stack2 và xóa phần tử ở đầu stack. Độ phức tạp của thời gian là O(n) cho dequeue, vì chúng tôi phải sao chép stack1 vào stack2. độ phức tạp thời gian của enqueue giống như một ngăn xếp thông thường

0
PradGar

Với O(1)dequeue(), tương tự như answer của pythonquick :

// time: O(n), space: O(n)
enqueue(x):
    if stack.isEmpty():
        stack.Push(x)
        return
    temp = stack.pop()
    enqueue(x)
    stack.Push(temp)

// time: O(1)
x dequeue():
    return stack.pop()

Với O(1)enqueue() (điều này không được đề cập trong bài đăng này vì vậy câu trả lời này), cũng sử dụng quay lui để tạo bong bóng và trả lại mục cuối cùng.

// O(1)
enqueue(x):
    stack.Push(x)

// time: O(n), space: O(n)
x dequeue():
    temp = stack.pop()
    if stack.isEmpty():
        x = temp
    else:
        x = dequeue()
        stack.Push(temp)
    return x

Rõ ràng, đó là một bài tập mã hóa tốt vì nó không hiệu quả nhưng vẫn thanh lịch.

0
hIpPy