9. 用两个栈实现队列

剑指offer 09 用两个栈实现队列

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTaildeleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

示例 1:

输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出: [null,null,3,-1]

示例 2:

输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]

提示:

1 < = v a l u e s < = 10000 1 <= values <= 10000 1<=values<=10000
最多会对 appendTaildeleteHead 进行 10000 次调用

解题思路

示例解释:
在这里插入图片描述
题目都已经告知是需要两个栈,那么这两个栈都应该设置成队列的成员属性,一个栈用于实际存放元素,而另一个栈就是用于各种操作时的中间容器而已。

解法一

元素永远保存在stack1(主栈中),stack2(辅助栈)只是作为一个中间容器。
在这里插入图片描述

class CQueue {
    
    
    //由于需要两个栈来实现队列,所以这两个栈应该是成员属性
    private Stack<Integer> stack1;//主栈
    private Stack<Integer> stack2;//辅助栈

    public CQueue() {
    
    
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    
    public void appendTail(int value) {
    
    
        stack1.push(value);
    }
    
    public int deleteHead() {
    
    
        if(stack1.isEmpty()) return -1;
        copy(stack1,stack2);//stack1的数据都移到stack2
        int res = stack2.pop();
        copy(stack2,stack1);//stack2的数据都移回stack1
        return res;
    }

    //定义一个辅助函数,用于把数据从一个栈移到另一个栈
    private void copy(Stack<Integer> stack1,Stack<Integer> stack2){
    
    
         while(!stack1.isEmpty()){
    
    
            int value = stack1.pop();
            stack2.push(value);
        }
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

在这里插入图片描述

显然,这里只需要进队和出队操作,所以每次移到stack2的数据是可以不用移回来了的。详见解法二。

解法二

stack1仍然是主栈,插入操作直接进stack1即可,而出队操作时,可以先判断stack2是否是空的,非空则直接出栈一个元素即是队列的对头元素,若是stack2空,则将stack1的元素都移到stack2,刚好又逆序,符合出队的顺序了。

class CQueue {
    
    
    //由于需要两个栈来实现队列,所以这两个栈应该是成员属性
    private Stack<Integer> stack1;
    private Stack<Integer> stack2;

    public CQueue() {
    
    
        stack1 = new Stack<>();
        stack2 = new Stack<>();
    }
    
    public void appendTail(int value) {
    
    
        stack1.push(value);
    }
    
    public int deleteHead() {
    
    
        //stack2空就将stack1的元素移过来,stack2非空则可以直接出栈,栈顶元素就是队首元素
        if(stack2.isEmpty()) {
    
    
            copy(stack1,stack2);//stack1的数据都移到stack2
        }
        //如果此时stack2还是空的,说明stack1和stack2中都没有元素,队列空
        if(stack2.isEmpty()) return -1;
        return  stack2.pop();//stack2非空,栈顶元素就是队首元素
    }

    //定义一个辅助函数,用于把数据从一个栈移到另一个栈
    private void copy(Stack<Integer> stack1,Stack<Integer> stack2){
    
    
         while(!stack1.isEmpty()){
    
    
            int value = stack1.pop();
            stack2.push(value);
        }
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

在这里插入图片描述


第九题扩展题1 用栈实现队列 LeetCode232

232. 用栈实现队列

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列的支持的所有操作(push、pop、peek、empty):

实现 MyQueue类:

void push(int x) 将元素x推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek()返回队列开头的元素
boolean empty() 如果队列为空,返回true ;否则,返回 false

说明:

你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size,is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

进阶:

你能否实现每个操作均摊时间复杂度为 O ( 1 ) O(1) O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O ( n ) O(n) O(n) ,即使其中一个操作可能花费较长时间。

示例:

输入:
[“MyQueue”, “push”, “push”, “peek”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:

MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

提示:

1 <= x <= 9
最多调用 100push、pop、peekempty
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

解题思路

解法一

解法与上面完全一致,同样提供两份对比代码和执行结果对比

class MyQueue {
    
    
    //由于需要两个栈来实现队列,所以这两个栈应该是MyQueue的属性
    private Stack<Integer> stack1 = new Stack<>();
    private Stack<Integer> stack2 = new Stack<>();
    
    /** Initialize your data structure here. */
    public MyQueue() {
    
    
        
    }
    
    //定义一个辅助函数,用于把数据从一个栈移到另一个栈
    private void copy(Stack<Integer> stack1,Stack<Integer> stack2){
    
    
        while(stack1.size()>0){
    
    
            int num = stack1.pop();
            stack2.push(num);
        }
    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {
    
    
        stack1.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
    
    
        copy(stack1,stack2);//stack1的数据都移到stack2
        int res = stack2.pop();
        copy(stack2,stack1);//stack2的数据都移到stack1
        return res;
    }
    
    /** Get the front element. */
    public int peek() {
    
    
        copy(stack1,stack2);//stack1的数据都移到stack2
       int res = stack2.peek();
       copy(stack2,stack1);//stack2的数据都移到stack1
       return res;
    }
    
    /** Returns whether the queue is empty. */
    public boolean empty() {
    
    
    //由于每次操作完之后数据总是存在stack1中的,所以判断stack1是否为空就可以了
        return stack1.isEmpty();
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */

在这里插入图片描述

解法二

元素移到stack2 进行操作后并不移回stack1写法如下

class MyQueue {
    
    
    //由于需要两个栈来实现队列,所以这两个栈应该是MyQueue的属性
    private Stack<Integer> stack1 = new Stack<>();
    private Stack<Integer> stack2 = new Stack<>();
    
    /** Initialize your data structure here. */
    public MyQueue() {
    
    
        
    }
    
    //定义一个辅助函数,用于把数据从一个栈移到另一个栈
    private void copy(Stack<Integer> stack1,Stack<Integer> stack2){
    
    
        while(stack1.size()>0){
    
    
            int num = stack1.pop();
            stack2.push(num);
        }
    }
    
    /** Push element x to the back of queue. */
    public void push(int x) {
    
    
        stack1.push(x);
    }
    
    /** Removes the element from in front of queue and returns that element. */
    public int pop() {
    
    
        //stack2空就将stack1的元素移过来,stack2非空则可以直接出栈,栈顶元素就是队首元素
        if(stack2.isEmpty()) {
    
    
            copy(stack1,stack2);//stack1的数据都移到stack2
        }
        //如果此时stack2还是空的,说明stack1和stack2中都没有元素,队列空
        if(stack2.isEmpty()) return -1;
        return  stack2.pop();//stack2非空,栈顶元素就是队首元素
    }
    
    /** Get the front element. */
    public int peek() {
    
    
        //stack2空就将stack1的元素移过来,stack2非空则可以直接peek,栈顶元素就是队首元素
        if(stack2.isEmpty()) {
    
    
            copy(stack1,stack2);//stack1的数据都移到stack2
        }
        //如果此时stack2还是空的,说明stack1和stack2中都没有元素,队列空
        if(stack2.isEmpty()) return -1;
        return  stack2.peek();//stack2非空,栈顶元素就是队首元素
    }
    
    /** Returns whether the queue is empty. */
    public boolean empty() {
    
    
        return stack1.isEmpty() && stack2.isEmpty();//两个栈都空就是队列空了
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */

在这里插入图片描述


第九题扩展题2 用两个队列实现栈 LeetCode225

225. 用队列实现栈

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)

实现 MyStack类:

void push(int x)将元素 x 压入栈顶。
int pop()移除并返回栈顶元素。
int top()返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false

注意:

你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、sizeis empty这些操作。
你所使用的语言也许不支持队列。 你可以使用list(列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:

输入:
[“MyStack”, “push”, “push”, “top”, “pop”, “empty”]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:

MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

提示:

1 <= x <= 9
最多调用100push、pop、topempty
每次调用 poptop 都保证栈不为空

进阶: 你能否实现每种操作的均摊时间复杂度为 O ( 1 ) O(1) O(1) 的栈?换句话说,执行 n 个操作的总时间复杂度 O ( n ) O(n) O(n) ,尽管其中某个操作可能需要比其他操作更长的时间。你可以使用两个以上的队列。

解题思路

与上面的题目思路基本是一样的,但是由于队列的先进先出性质,从queue1将元素移到queue2后,元素仍然是先进先出的,不符合栈的先进后出故这里没办法像上面的题一样让两个容器都放数据的解法了,只有一个队列用于实际存元素,另一个队列用于各种操作时的中间容器的解法

class MyStack {
    
    
    //用两个队列实现栈,两个队列就应该是MyStack的属性
    private Queue<Integer> queue1; //实际保存数据
    private Queue<Integer> queue2; //辅助容器

    /** Initialize your data structure here. */
    public MyStack() {
    
    
        queue1 = new LinkedList<>(); //LinkedList实现了Queue接口
        queue2 = new LinkedList<>();
    }
    
    /** Push element x onto stack. */
    public void push(int x) {
    
    
        //push进来的要放到队首,所以先判断queue1是否空,不空的话先都移走,等下再移回来
        //目的就是要让x放到queue1的队首,这样队首元素出队也刚好符合是栈顶元素出栈
        while(!queue1.isEmpty()){
    
    
            int num = queue1.peek();
            queue2.add(num);
            queue1.poll();
        }
        //此时queue1已空,可以放入x了
        queue1.add(x);
        //将queue2中的元素再放回来
        while(!queue2.isEmpty()){
    
    
            int num = queue2.peek();
            queue1.add(num);
            queue2.poll();
        }
    }
    
    /** Removes the element on top of the stack and returns that element. */
    public int pop() {
    
    
        //数据都在queue1中,而queue1的队首就是所谓的栈顶元素,即要弹出的元素
        return queue1.poll();
    }
    
    /** Get the top element. */
    public int top() {
    
    
        return queue1.peek();
    }
    
    /** Returns whether the stack is empty. */
    public boolean empty() {
    
    
        return queue1.isEmpty();
    }
}

/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

在这里插入图片描述

小结

这种用两个容器协调做事的题目,一般一个容器是主容器,存放实际元素,而另一个容器就是辅助容器,协助完成某些操作。

猜你喜欢

转载自blog.csdn.net/YouMing_Li/article/details/114262098