1. 包含min函数的栈
来源
leetcode155 https://leetcode.com/problems/min-stack/
剑指offer21
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
push(x) -- Push element x onto stack.
pop() -- Removes the element on top of the stack.
top() -- Get the top element.
getMin() -- Retrieve the minimum element in the stack.
注:这几个函数要在常数时间内完成。
思路:push,pop,top正常来说肯定是常数时间内的,但是如何让getMin也能保持在常数时间呢。
单独维护一个单调递减栈来存储栈中最小值。
class MinStack {
Deque<Integer> deque = new ArrayDeque<Integer>();
Deque<Integer> minDeque = new ArrayDeque<Integer>();
public void push(int x) {
deque.add(x);
if(minDeque.size() == 0 || x <= minDeque.peekLast()) minDeque.add(x);
}
public void pop() {
int x = deque.pollLast();
if(x == minDeque.peekLast()) minDeque.pollLast();
}
public int top() {
return deque.peekLast();
}
public int getMin() {
return minDeque.peekLast();
}
}
2. 用两个栈实现队列
维护两个栈,每次Push进一个栈中,每次getHead从另一个栈中取,当取栈空了,则将push栈中的弹入取栈中。这样保证了先进的一定比后进的先出。弹出元素的平均时间复杂度O(1)
class QueueImpl {
Deque<Integer> deque_push = new ArrayDeque<Integer>();
Deque<Integer> deque_pop = new ArrayDeque<Integer>();
public void push(int x) {
deque_push.add(x);
}
public int getHead() {
if(deque_pop.size() == 0){
while(deque_push.size() != 0) deque_pop.add(deque_push.pollLast());
}
return deque_pop.pollLast();
}
}
3. 用两个队列实现栈
维护两个队列,每次向有元素的队列压入元素(两个队列会交替压入),每次弹出元素时要将有元素队列中的元素放入另一个队列中,留下一个元素在原来的队列弹出。弹出的平均时间复杂度O(n)。
class StackImpl {
Deque<Integer> deque1 = new ArrayDeque<Integer>();
Deque<Integer> deque2 = new ArrayDeque<Integer>();
public void push(int x) {
Deque<Integer> deque = deque1.size() == 0 ? deque2 : deque1;
deque.add(x);
}
public int pop() {
Deque<Integer> deque_pop = deque1.size() == 0 ? deque2 : deque1;
Deque<Integer> deque_push = deque1.size() == 0 ? deque1 : deque2;
while(deque_pop.size() > 1) deque_push.add(deque_pop.pollLast());
return deque_pop.pollLast();
}
}
4. 用一个数组实现两个栈
说明如何用一个数组A[1..n]来实现两个栈,使得两个栈中的元素总和不到n时,两个都不会发生上溯。注意PUSH和POP操作的时间应为O(1)
思路:两个栈从数组的前后开始向中间存储。
class Stack {
int[] arr;
int flag1 = -1, flag2 = -1;
public Stack(n){
arr = new int[n];
flag2 = n;
}
public void push1(int x) {
if(flag1 == (flag2 - 1)) return;
arr[++flag1] = x;
}
public void push2(int x) {
if(flag1 == (flag2 - 1)) return;
arr[--flag2] = x;
}
public int pop1() {
if(flag1 > -1) return arr[flag1--];
}
public int pop2() {
if(flag2 < n) return arr[flag2++];
}
}
思考:用一个数组实现三个栈呢。
可以使用交叉索引,像上一题存两个栈的话,一个栈可以存储在0,2,4,6…/另一存储在1,3,5,7…
若三个栈就可以存储成0,3,6,9…./1,4,7,10…/2,5,8,11….
若还沿用上题方法,可以将第三个栈存储为:mid, mid+1, mid-1, mid+2, mid-2这样对称存储。
5. 栈对递归思想的考察
翻转栈中所有元素,要求额外空间复杂度o(1)
用递归来实现(与汉诺塔思想类似)
首先弹出栈顶元素top1,将剩余n-1栈递归反转,反转后弹出当前栈顶元素top2,再递归反转n-2栈。
放入top1元素,反转n-1栈。
最后放入top2元素。
public void reverseStack(Deque<Integer> deque){
if(deque.size() == 0) return;
int top1 = deque.pollLast();
reverseStack(deque);
if(deque.size() == 0) deque.add(top1);
else{
int top2 = deque.pollLast();
reverseStack(deque);
deque.add(top1);
reverseStack(deque);
deque.add(top2);
}
}
-给栈中元素排序
类似冒泡排序的递归思想,每次将最小的值弹到栈顶,然后从下一个元素开始进行下轮的冒泡递归
public void sortStack(Deque<Integer> deque){
if(deque.size() == 0) return;
int top1 = deque.pollLast();
sortStack(deque);
if(deque.size() == 0) deque.add(top1);
else{
int top2 = deque.pollLast();
if(top1 <= top2) {
deque.add(top2);
deque.add(top1);
}
else{
deque.add(top1);
sortStack(deque);
deque.add(top2);
}
}
}