关于算法:如何使用两个堆栈实现队列?

How to implement a queue using two stacks?

假设我们有两个堆栈,没有其他临时变量。

是否可以仅使用两个堆栈"构造"队列数据结构?


保留两个堆栈,我们称它们为inboxoutbox

排队:

  • 将新元件推到inbox

出列:

  • 如果outbox是空的,则从inbox中弹出每个元素并将其推到outbox上,重新填充。

  • 弹出并返回EDOCX1[1]的顶部元素

使用这种方法,每个元素将恰好在每个堆栈中出现一次,这意味着每个元素将被推两次,弹出两次,从而实现摊余的固定时间操作。

下面是Java中的一个实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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();
    }

}


A-如何反转堆栈

要了解如何使用两个堆栈构建队列,您应该了解如何将堆栈反向透明。记住堆叠是如何工作的,它非常类似于你厨房里的菜堆。最后一道洗过的盘子将放在干净的架子上,这在计算机科学中被称为后进先出(lifo)。好的。

让我们想象一下我们的叠像一个瓶子下面;好的。

enter image description here。好的。

如果我们分别推整数1、2、3,那么3将位于堆栈的顶部。因为1将首先被推,然后2将被放在1的顶部。最后,将3个放在堆栈的顶部,以瓶子表示的堆栈的最新状态如下;好的。

氧化镁好的。

现在我们有了一个用值3、2、1填充的瓶子来表示的堆栈。我们要反转堆栈,使堆栈的顶部元素为1,而堆栈的底部元素为3。我们能做什么?我们可以拿着瓶子,把它倒置,这样所有的值都应该按顺序颠倒?好的。

氧化镁好的。

是的,我们能做到,但那是一瓶。要执行相同的过程,我们需要有第二个堆栈,它将以相反的顺序存储第一个堆栈元素。让我们把填充的堆栈放在左边,把新的空堆栈放在右边。为了颠倒元素的顺序,我们将从左堆栈中弹出每个元素,并将它们推到右堆栈中。在下面的图片中,您可以看到我们这样做时会发生什么;好的。

氧化镁好的。

所以我们知道如何反转堆栈。好的。B-使用两个堆栈作为队列

在前一部分中,我已经解释了如何反转堆栈元素的顺序。这一点很重要,因为如果我们将元素推送并弹出到堆栈中,那么输出将完全按照队列的相反顺序进行。考虑到一个例子,让我们把整数数组{1, 2, 3, 4, 5}推到堆栈中。如果我们弹出元素并打印到堆栈为空,我们将按相反的顺序得到数组,这将是{5, 4, 3, 2, 1}。记住,对于相同的输入,如果我们将队列出列直到队列为空,那么输出将是{1, 2, 3, 4, 5}。因此很明显,对于相同的元素输入顺序,队列的输出与堆栈的输出正好相反。正如我们知道如何使用一个额外的堆栈反转一个堆栈,我们可以使用两个堆栈构造一个队列。好的。

我们的队列模型将由两个堆栈组成。一个堆栈将用于enqueue操作(左边的堆栈1将被称为输入堆栈),另一个堆栈将用于dequeue操作(右边的堆栈2将被称为输出堆栈)。查看下图;好的。

氧化镁好的。

我们的伪代码如下:好的。排队操作

1
Push every input element to the Input Stack

出列操作

1
2
3
4
5
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

让我们分别将整数{1, 2, 3}排队。整数将被推送到位于左侧的输入堆栈(堆栈1)上;好的。

enter image description here。好的。

那么,如果我们执行一个出列操作会发生什么呢?每当执行出列操作时,队列将检查输出堆栈是否为空(参见上面的伪代码),如果输出堆栈为空,则将在输出上提取输入堆栈,以便反转输入堆栈的元素。返回值之前,队列的状态如下:好的。

氧化镁好的。

查看输出堆栈(堆栈2)中元素的顺序。很明显,我们可以从输出堆栈中弹出元素,这样输出就如同从队列中出列一样。因此,如果我们执行两个出列操作,首先我们将分别得到{1, 2}。然后元素3将是输出堆栈的唯一元素,输入堆栈将为空。如果我们将元素4和5排队,那么队列的状态如下:好的。

氧化镁好的。

现在输出堆栈不是空的,如果我们执行一个出列操作,只有3个会从输出堆栈中弹出。那么国家将被视为如下:好的。

氧化镁好的。

同样,如果我们在第一个出列操作上再执行两个出列操作,队列将检查输出堆栈是否为空,这是真的。然后弹出输入堆栈的元素并将其推送到输出堆栈,直到输入堆栈为空,那么队列的状态如下所示;好的。

氧化镁好的。

很容易看出,两个出列操作的输出将是{4, 5}。好的。C-使用两个堆栈构造的队列的实现

这里是爪哇的一个实现。我不打算使用栈的现有实现,所以这里的示例将重新设计轮子;好的。c-1)mystack类:一个简单的堆栈实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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("
");
    }
}

c-2)myqueue类:使用两个堆栈的队列实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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)演示代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)样本输出

1
2
3
4
5
Dequeued: 1
Dequeued: 2
Dequeued: 3
Dequeued: 4
Dequeued: 5

。好啊。


甚至可以只使用一个堆栈来模拟队列。第二个(临时)堆栈可以由对insert方法的递归调用的调用堆栈模拟。

将新元素插入队列时,原则保持不变:

  • 您需要将元素从一个堆栈转移到另一个临时堆栈,以颠倒它们的顺序。
  • 然后将要插入的新元素推送到临时堆栈上
  • 然后将元素转移回原始堆栈
  • 新元素将位于堆栈的底部,最旧的元素位于顶部(首先弹出)

仅使用一个堆栈的队列类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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();
    }
}


不过,时间的复杂性会更糟。一个好的队列实现可以在固定的时间内完成所有事情。

编辑

不知道为什么我的答案在这里被否决了。如果我们编程,我们关心时间的复杂性,并且使用两个标准栈来生成一个队列是低效的。这是一个非常有效和相关的观点。如果有人觉得有必要更多地投反对票,我会有兴趣知道为什么。

更详细一点:关于为什么使用两个堆栈比只使用一个队列更糟糕:如果使用两个堆栈,并且有人在发件箱为空时调用dequeue,则需要线性时间才能到达收件箱的底部(如Dave代码中所示)。

可以将队列实现为一个单独链接的列表(每个元素指向下一个插入的元素),保留一个指向最后一个插入元素的额外指针以进行推送(或使其成为循环列表)。在这个数据结构上实现队列和出列是非常容易的。最坏的情况是固定时间,而不是摊销。而且,正如评论似乎要求澄清的那样,最坏情况下的固定时间比摊销固定时间要好得多。


让要实现的队列为q,用于实现q的堆栈为stack1和stack2。

Q可以通过两种方式实现:

方法1(使排队操作成本高昂)

此方法确保新输入的元素始终位于堆栈1的顶部,以便从堆栈1中弹出出列操作。要将元素放在stack1的顶部,使用stack2。

1
2
3
4
5
6
7
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.

方法2(使出列操作成本高昂)

在这个方法中,在队列操作中,新元素被输入stack1的顶部。在去队列操作中,如果stack2为空,则所有元素都移到stack2,最后返回stack2的顶部。

1
2
3
4
5
6
7
8
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.

方法2肯定比方法1好。方法1在排队操作中将所有元素移动两次,而方法2(在出列操作中)仅在stack2为空时移动元素一次。


C中的解决方案#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 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();
        }
}


您必须从第一个堆栈中弹出所有内容才能获得底部元素。然后将它们全部放回第二个堆栈中,以执行每个"出列"操作。


对于C开发人员,以下是完整的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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();
        }
    }
}


队列中的两个堆栈定义为stack1和stack2。

排队:euqueued元素总是被推入stack1

出列:stack2的顶部可以弹出,因为它是stack2不为空时插入队列的第一个元素。当stack2为空时,我们从stack1中弹出所有元素,并逐个将它们推入stack2。队列中的第一个元素被推入stack1的底部。它可以在弹出和推送操作后直接弹出,因为它位于Stack2的顶部。

下面是相同的C++示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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;
}

这个解决方案是从我的博客中借用的。在我的博客网页上可以看到更详细的分析和逐步的操作模拟。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 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。使排队过程的成本更高2。或者使出列过程成本更高

https://www.geeksforgeks.org/queue-using-stacks网站/

我从上面的文章中发现的一个重要方法是使用堆栈数据结构和递归调用堆栈构造队列。

虽然可以说,字面上这仍然使用两个堆栈,但理想情况下,这只使用一个堆栈数据结构。

问题的解释如下:

  • 声明一个用于将数据排队和排出位的堆栈,并将数据推入堆栈。

  • 而出列有一个基本条件,当堆栈的大小为1时,将弹出堆栈的元素。这将确保在出列递归期间没有堆栈溢出。

  • 先从栈顶弹出数据。理想情况下,这个元素将是位于堆栈顶部的元素。现在,完成后,递归地调用dequeue函数,然后将上面弹出的元素推回到堆栈中。

  • 代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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;

    通过这种方式,可以使用单个堆栈数据结构和递归调用堆栈创建队列。


    使用swift中的两个栈实现队列:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    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()!)
                }
            }
        }
    }


    我将在Go中回答这个问题,因为Go的标准库中没有丰富的集合。

    因为一个栈很容易实现,我想我应该尝试使用两个栈来完成一个双端队列。为了更好地理解我是如何得出答案的,我将实现分为两部分,希望第一部分更容易理解,但它是不完整的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    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:]
        }
    }

    它基本上是两个栈,我们允许栈底互相操作。我还使用了STL命名约定,在这里,栈的传统push、pop、peek操作都有一个前/后前缀,不管它们是指队列的前面还是后面。

    上面的代码的问题是它不能非常有效地使用内存。事实上,它不断地增长,直到你耗尽空间。真的很糟糕。解决这个问题的方法是尽可能简单地重用堆栈空间的底部。我们必须引入一个偏移量来跟踪这一点,因为一旦收缩,Go中的一个切片就不能在前面增长。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    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.")
            }
        }
    }

    它有很多小功能,但在6个功能中,3个只是另一个功能的镜子。


    下面是使用ES6语法的JavaScript语言解决方案。

    堆栈.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //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 };

    队列使用wostocks.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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 };

    用法如下:

    索引.js

    1
    2
    3
    4
    5
    6
    7
    8
    import { StackUsingTwoQueues } from './StackUsingTwoQueues';

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

    console.log(que.dequeue());  //output:"A"


    这是我在使用LyKeDLIST的Java解决方案。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    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;
        }
    }

    }

    注意:在这种情况下,pop操作非常耗时。所以我不建议使用两个堆栈创建队列。


    对于O(1)dequeue(),与pythonquick的答案相同:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 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()

    对于O(1)enqueue()(这在本文中没有提到,所以这个答案),它也使用回溯来冒泡并返回最底部的项目。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 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

    显然,这是一个很好的编码练习,因为它效率很低,但是很优雅。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    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();
        }


    }

    对于每个排队操作,我们将添加到stack1的顶部。对于每一个出列,我们将stack1的内容清空到stack2中,并删除堆栈顶部的元素。对于出列,时间复杂度为o(n),因为我们必须将stack1复制到stack2。排队的时间复杂度与常规堆栈相同


    使用两个java.util.stack对象进行队列实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public final class QueueUsingStacks<E> {

            private final Stack<E> iStack = new Stack<>();
            private final Stack<E> oStack = new Stack<>();

            public void enqueue(E e) {
                iStack.push(e);
            }

            public E dequeue() {
                if (oStack.isEmpty()) {
                    if (iStack.isEmpty()) {
                        throw new NoSuchElementException("No elements present in Queue");
                    }
                    while (!iStack.isEmpty()) {
                        oStack.push(iStack.pop());
                    }
                }
                return oStack.pop();
            }

            public boolean isEmpty() {
                if (oStack.isEmpty() && iStack.isEmpty()) {
                    return true;
                }
                return false;
            }

            public int size() {
                return iStack.size() + oStack.size();
            }

    }