剑指 Offer 09. 用两个栈实现队列
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,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 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用
解法一:
stack1用存数据,stack2用来辅助取数据,取数据时,如果stack1为空,直接返回-1,否则先把所有元素压入到stack2中,弹出并记录stack2栈顶元素,把stack2所有元素再压回到stack1
1 class CQueue { 2 3 Stack<Integer> stack1; 4 Stack<Integer> stack2; 5 6 public CQueue() { 7 stack1 = new Stack<Integer>(); 8 stack2 = new Stack<Integer>(); 9 } 10 11 public void appendTail(int value) { 12 stack1.push(value); 13 } 14 15 public int deleteHead() { 16 // 如果stack1为空,直接返回-1 17 if(stack1.isEmpty()){ 18 return -1; 19 }else{ 20 // 否则先把所有元素压入到stack2中 21 while(!stack1.isEmpty()){ 22 stack2.push(stack1.pop()); 23 } 24 // 弹出并记录栈顶元素 25 int top = stack2.pop(); 26 27 // 把stack2所有元素再压回到stack1 28 while(!stack2.isEmpty()){ 29 stack1.push(stack2.pop()); 30 } 31 return top; 32 } 33 } 34 }
leetcode运行时间为332ms, 空间为46.7mb, 这个时间太恐怖了
复杂度分析:
时间复杂度:插入元素的复杂度为O(1), 删除元素的复杂度为O(2n)
空间复杂度:两个栈, 所以为O(2n)
解法二:
两个栈,一个用来存,一个用来取,两个栈的元素之和等于当前所有元素。stack2不是取元素的辅助栈,而是专门用来取元素,取元素时,先判断 stack2是否为空,如果为空,把stack1中的所有元素都搬运到stack2,此时stack2的栈顶到栈底的元素排列顺序就是将来取元素的顺序
1 class CQueue { 2 3 Stack<Integer> stack1; 4 Stack<Integer> stack2; 5 6 public CQueue() { 7 stack1 = new Stack<Integer>(); 8 stack2 = new Stack<Integer>(); 9 } 10 11 public void appendTail(int value) { 12 stack1.push(value); 13 } 14 15 public int deleteHead() { 16 if(stack2.isEmpty()){ 17 while(!stack1.isEmpty()){ 18 stack2.push(stack1.pop()); 19 } 20 } 21 if(stack2.isEmpty()){ 22 return -1; 23 }else{ 24 // 如果不为空,直接返回stack2的栈顶元素 25 return stack2.pop(); 26 } 27 } 28 }
leetcode运行时间为55ms, 空间为46.5mb, 这个时间已经好多了
复杂度分析:
时间复杂度:存元素的时间为O(1), 取元素虽然有时需要把stack1 的所有元素都拷贝到 stack2, 但是每个元素只会入栈和出栈 stack2 一次,所以取元素的平均时间也为O(1)
空间复杂度:两个栈,一个用来存,一个用来取,两个栈的元素之和等于当前所有元素,所以空间复杂度为O(n)