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(双端队列)来模拟一个栈,只要是标准的栈操作即可。
代码(版本1)
class MyQueue {
Stack<Integer> in;
Stack<Integer> out;
public MyQueue() {
in = new Stack<>();
out = new Stack<>();
}
public void push(int x) {
in.push(x); //输入栈模拟队列的输入
}
public int pop() {
if(out.isEmpty()){ //输出栈模拟队列的输出
while(!in.isEmpty()){ //如果输入栈为空,把输入栈元素全部移入输出栈
out.push(in.pop());
}
}
int result = out.pop();
return result;
}
public int peek() {
int result = this.pop(); //函数复用,调用上面的pop
out.push(result); //把pop的元素push回去
return result;
}
public boolean empty() {
return in.isEmpty() && out.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)
class MyQueue {
Stack<Integer> in;
Stack<Integer> out;
public MyQueue() {
in = new Stack<>();
out = new Stack<>();
}
public void push(int x) {
in.push(x); //输入栈模拟队列的输入
}
public int pop() {
tackle(); //把out栈操作成我们想要的状态,即满足队列输出顺序
int result = out.pop();
return result;
}
public int peek() {
tackle(); 把out栈操作成我们想要的状态,即满足队列输出顺序
int result = out.peek();
return result;
}
public boolean empty() {
return in.isEmpty() && out.isEmpty();
}
//用于处理输入栈in到输出栈out的元素移动
public void tackle(){
//如果out是空,要把in的所有元素移过去
if(out.isEmpty()){
while(!in.isEmpty()){
out.push(in.pop());
}
}
//如果out不是空,可以直接输出,不需要移动元素
else{
return;
}
}
}
/**
* 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();
*/
总结
1.思路
队列的队尾输入元素,队头输出元素。那么,可以用一个输入栈,模拟队列的输入,用一个输出栈,模拟队列的输出。而且,输入栈的元素和队列的队尾输入元素顺序一致。但输出栈的元素如果要和输入顺序一致,需要从输入栈从移入。下面举例说明了如何用输入栈和输出栈模拟队列,以及两个栈的元素如何移动。
2.算法流程
(1)push函数
直接in.push(x)即可,把队列的队尾元素push到in栈内。
(2)pop函数
由于输入栈的元素顺序不是我们想要的输出顺序,因此要把in栈的元素pop出来再push到out栈里。
首先,判断当前的out栈是否为空。为什么要做这个判断?因为如果out是空,说明out中没有元素输出了,要把in中的所有元素移动过来。因此,如果out为空,就要用while循环判断in,只要in栈不为空,需要一直执行out.push(in.pop())语句。
此时,out栈就处理到我们希望的状态了,要么out本身有元素可以直接输出,要么out没有元素但是已经把in的所有元素拿过来了。此时就可以返回out.pop()。
(3)peek函数
peek函数用于获取队头的元素,即获取输出栈的元素。和pop函数的原理一致,需要先把out栈就处理到我们希望的状态。然后此时就可以返回out.peek()
(4)isEmpty函数
很简单,只有满足in和out同时为空,才能返回true。
3.注意点
这里我给了两个版本的Java代码。
版本一:直接把“out栈处理到我们希望的状态”功能的代码写在了pop函数里,在peek函数中,直接用this指针调用pop函数获取队头元素,再把队头元素push回去即可。
版本二:把“out栈处理到我们希望的状态”功能的代码封装到了单独的tackle函数里。因此在pop函数中,先调用tackle处理out和in的状态,返回out.pop()即可。在peek函数里,先调用tackle处理out和in的状态,返回out.peek()即可。
两个版本相比较,我觉得版本一在第一遍写代码时更容易写出来,但是版本二的代码逻辑更清晰,相当于把pop和peek需要的相同功能——处理out和in栈的状态,单独封装成函数,然后在单独调用。如果有能力,还是写版本二更好,方便后续对tackle函数进行修改。
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
、size
和is empty
这些操作。 - 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
代码(一个队列实现)
class MyStack {
Queue<Integer> que;
public MyStack() {
que = new LinkedList<>(); //双向链表
}
public void push(int x) {
que.add(x); //add添加到列表末尾,模拟栈的输入
}
public int pop() {
tackle(); //如果队列元素大于1,要把前面的元素依次移到队尾
int result = que.poll(); //poll用于删除队头元素
return result;
}
public int top() {
//以123为例,要想获取队尾元素3,先移动成312,把队尾元素3给poll,变成12,获取到3
//再把3重新add到队尾,变成123,恢复初始状态
tackle(); //如果队列元素大于1,要把前面的元素依次移到队尾
int result = que.poll(); //poll删除队头元素,并返回队头元素
que.add(result); //把删除的元素重新加到队尾,
return result;
}
public boolean empty() {
return que.isEmpty();
}
//如果队列元素大于1,要把前面的元素依次移到队尾
public void tackle(){
int size = que.size();
while(size-- > 1){
que.add(que.poll());
}
}
}
/**
* 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();
*/
总结
1.思想
要想用一个队列实现栈,首先考虑栈的输入123,等价于队列的末尾输入123。然后考虑栈的输出321,是从栈顶(队列末尾)开始输出即先输出3。因此,关键是要把队列的元素进行移动调整,把末尾元素3移动到队头,把前面的元素12移动到队尾。下图展示了栈输入123输出321,在一个队列中如何进行输入和输出。
2.算法流程
(1)push函数
直接用add函数,把元素放到队列的末尾即可
(2)pop函数
首先要明确,栈中的pop元素是在栈顶的,也是在队列的末尾的。由于队列只能在队头输出,因此我们把队尾元素移动队头不就好了。(队列123,要pop3,就把3移到最前面,变成312)
因此,要想pop队列的末尾元素3,那我们就先把前面的size-1个元素12移动到队尾,使得队尾元素移到队头,这时队列是312,再调用poll函数,pop出元素3。
(3)top函数
top函数是要返回栈顶元素,也是在队列的末尾。因此,我们操作的函数队列的队尾元素。那么可以和pop函数一样,初始队列状态是123,要想获取元素3,先把队尾元素3移到前面变成312,把队头元素pop出去获取到元素3,再把3重新add到队尾,变成123恢复队列初始状态。
(4)isEmpty函数
直接判断队列是否为空即可。
3.注意点
因为pop和top函数有相同的功能,即要把前size-1个元素移到队尾使得队尾元素移动到队头。因此我用了tackle函数进行了封装,实现队列的元素移动。然后,在pop函数里,先调用tackle把队尾元素移到前面,在进行poll即可。在top函数里,先调用tackle把队尾元素移到前面,在进行poll,最后把poll的元素重新add到队尾即可。
4.语法点
(1)queue.poll():用于删除的头部元素
(2)queue.add(x):用于在尾部添加元素
(3)queue.isEmpty():用于判断是否为空