《算法(第四版)》1.4.27:两个栈实现的队列。
思路a:有两个栈s和st,s视为栈,st视为队列,st.push(s.pop())实现item排列顺序的变换。添加item时,item先进入栈s,再通过对应关系重置“队列”st;删除item时,item先从“队列”st弹出,再通过对应关系重置栈s。对s和st的重置需要用到栈的复制。
阅读了这篇博客后又有了如下总结https://blog.csdn.net/synapse7/article/details/18133787
思路b:思路a实际上用了四个栈,转换的时候对栈的遍历次数过多,栈s和st的利用率低。可以考虑联用栈s和栈st一起表示队列。将栈s视为队列的逆序,st作为缓冲区,入队时,item直接压入s;出队时,将s中的item逐个倒入st,完成后弹出st顶部的item,再将st的item倒回s。所以s和st非空即满。
思路c:思路b中栈s和栈st的联用效率低,有些“倒回”操作是多余的。如果连续进行出队操作,则无需将st倒回s。所以在入队操作前判断s是否为空,若为空,则将st倒回s;在出队操作前判断st是否为空,若为空,则将s倒入st。
思路d:考虑到st中的item总是s倒入的,假设在“倒出”操作完成后,直接往s中压入新的item,会发现st中的item重要先于s中的item入队,故在st完全为空后,可以再进行“倒出”操作。简言之,不论s是否为空,可以直接进行item压入。Java程序如下
public class QueueStack<Item> {
private Stack<Item> s = new Stack<Item>();
private Stack<Item> st = new Stack<Item>();
public boolean isEmpty() {
return st.isEmpty() && s.isEmpty();
}
public int size() {
return st.size() + s.size();
}
public void enqueue(Item item) {
//像表尾添加元素
/***思路c
if(s.isEmpty()) { //非空即满
while(!st.isEmpty()) {
s.push(st.pop());
}
s.push(item);
} else {
s.push(item);
} **/
s.push(item);
}
public Item dequeue() {
//从表头删除元素
if(st.isEmpty()) { //非空即满
while(!s.isEmpty()) {
st.push(s.pop());
}
return st.pop();
} else {
return st.pop();
}
}
}
《算法(第四版)》1.3.49:栈与队列。用有限个栈实现一个队列,保证每个队列(在最坏的情况下)都只需要常数次的栈操作。
用两个栈表示队列,最坏的情况下,时间复杂度为O(N),比如第一次压入k个数,再弹出时需要进行k次倒出操作。此题实现将另起一篇介绍。https://blog.csdn.net/MaaaMalik/article/details/94163051