题目上的这两题可以说是经典题目了:
剑指 Offer 09: 双栈实现队列
leetcode 225: 队列实现栈
有关于栈和队列,我们之前也讨论过:
从零开始手撕一个数据结构(2)——基于链表实现栈和队列
我们都知道,栈和队列,一个是LIFO,一个是FIFO,那么怎么用栈去实现队列,用队列去实现栈呢?
我们知道,每次从栈中取一个元素都是从栈顶取的,如果我们要用栈实现一个队列,那这个【栈实现的队列】中每次【出队】的元素都是在栈底中取的,怎么样才能取到栈底元素呢?
剑指Offer 09 的题目要求是:用两个栈实现队列
用两个栈的话就很好解决【如何取栈底元素】这个问题了:
题目要求是:
实现appendTail 和 deleteHead 两个方法, 分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
其中,appendTail
相当于队列中的offer
,即入队,而deleteHead
则相当于队列中的poll
,即出队,按照我们刚才的思路来,很容易就可以解决这个问题
public class StackImplQueue {
/**
* 这里用了java.util.Stack的栈,在速度上会比较慢,用LinkedList会快些,但没必要^_^
*/
Stack<Integer> s1;
Stack<Integer> s2;
public StackImplQueue() {
s1 = new Stack<>();
s2 = new Stack<>();
}
}
这里用了Stack,不用其它容器是因为其它容器的实现乱七八糟的
刚才我们的思路中,有将栈1元素全部取出放到栈2的操作,我称其为【栈的“倒腾”】,这就像你把一箱玩具倒到另一箱,原本在压箱底的玩具就会在另一箱的箱顶,具体操作如下
/**
* 将栈1倒腾到栈2上的方法
*/
public void stackTransfer() {
while (!s1.isEmpty()) {
s2.push(s1.pop());
}
}
这里栈2一直接受栈1的出栈元素,直到栈1为空,应该很好理解吧?
/**
* 添加操作时,往栈1添加元素
*
* @param value
*/
public void appendTail(int value) {
s1.push(value);
}
前面说了,栈1就是用来存东西的,所以入队时直接在栈1入栈就行了
/**
* 删除操作时,当两个栈都为空,返回-1
* 当栈2为空,把栈1的元素倒腾到栈2上,再用栈2出栈(栈反转就是队列)
*
* @return
*/
public int deleteHead() {
if (s1.isEmpty() && s2.isEmpty()) {
return -1;
}
if (s2.isEmpty()) {
stackTransfer();
}
return s2.pop();
}
和刚才的操作一样,只有当栈2取空的时候才需要倒腾栈1,不然就乱序了
那么,用队列如何实现栈呢?先看看【leetcode 225: 队列实现栈】的题目要求:
你只能使用队列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 这些操作是合法的。
你所使用的语言也许不支持队列。 你可以使用 list 或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。
题目只对队列的操作做了要求,对实现方法并无特殊要求,既然刚才都用两个栈实现队列了,这次也用两个队列实现栈吧
boolean status
,当status==false
时,往队列1装数据,反之则往队列2装数据status
进行取反操作,以表示当前是哪个队列在存储数据题目要求:
使用队列实现栈的下列操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空
只需要设计这四个方法就行了
public class QueueImplStack {
Queue<Integer> q1;
Queue<Integer> q2;
int size;
/**
* 容器状态,当status==false时,q1为元素放置队列,否则q2为元素放置队列
*/
boolean status;
/** Initialize your data structure here. */
public QueueImplStack() {
this.q1 = new LinkedList<>();
this.q2 = new LinkedList<>();
}
}
/** Returns whether the stack is empty. */
public boolean empty() {
return size==0;
}
/** Push element x onto stack. */
public void push(int x) {
// 向元素放置队列添加新元素
if(status){
q2.offer(x);
}else{
q1.offer(x);
}
++size;
}
当status为false时,向队列1入队,反之向队列2入队
由于是两个队列实现的栈,数据的移动也是必须的
/**
* 队列转换,将当前放置元素所用的队列poll到另一条空队列,直到当前队列仅剩一个元素
* pop/top操作取栈顶元素时使用
*/
private void queueTransfer(){
for(int tmpSize=size-1;tmpSize>0;--tmpSize){
if(status){
q1.offer(q2.poll());
}else{
q2.offer(q1.poll());
}
}
}
获取size的个数再减1,然后循环出队入队
/** Removes the element on top of the stack and returns that element. */
public int pop() {
queueTransfer();
// 根据状态,取当前放置元素队列仅剩的一个元素并出队
int res = status?q2.poll():q1.poll();
// 更改状态,使当前放置元素队列更改为另一队列
status=!status;
--size;
return res;
}
/** Get the top element. */
public int top() {
queueTransfer();
// 根据状态,取当前放置元素队列仅剩的一个元素
int res = status?q2.peek():q1.peek();
// 将当前放置元素队列的最后一个元素移动到另一队列
if(status){
q1.offer(q2.poll());
}else{
q2.offer(q1.poll());
}
// 更改状态,使当前放置元素队列更改为另一队列
status=!status;
return res;
}
与出栈方法基本一致,只不过这里不对队尾元素进行出队,而是用peek取值之后再将其放到另一队列
由于在 leetcode 都通过了,就不测试了
这两题是比较经典的题目,我的方法也不是最好的,有更好方法的同学欢迎留言交流^_^