定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。
示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.min(); --> 返回 -2.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof
起初我只考虑到了只要调用min函数,就可以知道栈中的最小元素,并且时间复杂度是O(1),那么只要保证最小值在栈顶就好了,然后我居然借助一个辅助栈实现栈的出栈序列是一个升序序列。虽然可以实现栈顶元素是最小值,但是最后提交的时候发现是错误的,因为它的题目要求是出栈序列是基于入栈的时候,如果将栈进行排序的话,那么出栈序列就没有办法达到要求
。
正确的思路:
定义两个栈,其中一个栈stack用于正常的入栈、出栈操作,另一个栈stack2是用于存放最小值。
①在执行push操作的时候,stack直接入栈,但是stack2需要进行一定的条件判断,如果当前插入的元素x小于等于stack2的栈顶元素,或者当前的stack2是空的,那么直接将x压入栈stack2中。
②进行pop的操作的时候,stack直接调用pop方法,将栈stack中的栈顶元素出栈,并将这个栈顶元素赋值给y,然后我们判断y是否等于stack2中的栈顶元素,如果相等,那么我们需要将stack2中的栈顶元素跳出。为什么y等于stack2的栈顶元素的时候,需要从stack2跳出栈顶元素咧?因为执行y == stack2.peek(),主要是想判断当前从stack中跳出的元素是否为最小值,如果是最小值,那么我们就需要同时从stack2中跳出,从而保证栈的最小值不再是y了。
③执行top操作的时候,是基于入栈操作进行的,所以stack调用peek方法。
④执行min操作的时候,那么stack2调用peek方法即可。因为stack2的栈顶元素就是栈的最小值。
对应的代码:
import java.util.Stack;
class MinStack {
Stack<Integer> stack ;//定义一个栈,最后出栈序列是一个升序序列
Stack<Integer> stack2;//用于存放临时的栈顶元素
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<Integer>();
stack2 = new Stack<Integer>();
}
public void push(int x) {
if(stack2.empty() || stack2.peek() >= x){
//如果stack2为空,或者小于等于stack2中的栈顶元素,那么就将x压入stack2中
stack2.add(x);
}
stack.add(x);
}
public void pop() {
int x = stack.pop();
if(x == stack2.peek()){
/*
如果从栈stack中跳出的元素同样等于stack2的栈顶元素,那么我们将这个
元素从stack2中跳出,因为stack中的最小值不在是x了,为了保证一致性,所
以需要stack2中同时需要跳出x
*/
stack2.pop();
}
}
public int top() {
return stack.peek();//由于是基于入栈时的先后顺序的,所以这里是stack调用peek方法
}
public int min() {
return stack2.peek();//stack2是存放最小值,并且栈顶元素是最小值,所以是返回stack2的栈顶元素
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.min();
*/
运行结果
为了更好的理解stack2的作用,我们在执行push操作的时候可以是这样子操作:
如果stack2是空的或者新插入的元素x小于等于stack2的栈顶元素,那么就将x压入栈stack2中,否则就再次将栈顶元素压入stack2。
这样操作的话,相应的,在执行pop操作的时候,就不再需要向上面那样了,将x和stack2的栈顶元素进行比较,判断是否相等了,直接将stack2的栈顶元素跳出。
看起来好像不太懂?请看下面的分析:
对应的代码:
import java.util.Stack;
class MinStack {
Stack<Integer> stack ;//定义一个栈,存放入栈序列
Stack<Integer> stack2;//用于存放最小值,并且最小值在栈顶中
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<Integer>();
stack2 = new Stack<Integer>();
}
public void push(int x) {
stack.add(x);//stack直接入栈
/*
如果stack2为空,或者stack2的栈顶元素大于等于x,那么x压入stack2中,
否则,stack2的栈顶元素小于x,那么就再次将stack2的栈顶元素压入
stack2中,保证了两个栈的长度相同,同时保证了最小值没有发生改变
*/
if(stack2.empty()){
stack2.add(x);
}else{
if(stack2.peek() >= x)
stack2.add(x);
else
stack2.add(stack2.peek());
}
}
public void pop() {
/*
两个栈同时进行pop操作,不仅可以保证两个栈的长度相等,同时保证了最小值
的更新
*/
stack.pop();
stack2.pop();
}
public int top() {
return stack.peek();
}
public int min() {
return stack2.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.min();
*/
运行结果: