前言
在开始最小栈的算法之前,我们先来了解一下java中是如何实现数据结构栈的。
Stack继承自线程安全的数组Vector,既然是继承关系,那Stack也会拥有Vector声明为public的方法和变量。即Stack的基因是支持动态扩容的。但Stack出于更高层次的抽象,不提供这样的方法,而交由加强版的Deque队列实现,可见,栈不过是加了限制的队列。
我们不妨顺着顶层注释顺藤摸瓜,看看ArrayDeque的概况。
显然,它支持动态扩容,同时也提供了push,pop,peek,poll
等操作,既然栈可以用数组实现,那链表也自然可以——
我们发现LinkedList
真是一个全能选手,它即是List
,又是Queue
,还是Deque
。但是我们在使用的时候,总是用特定的接口来引用它,这是因为持有接口说明代码的抽象层次更高,而且接口本身定义的方法代表了特定的用途。
说这么多,核心的思想是想告诫大家,别在任何场景下都去写:Stack<Object> stack = new Stack<>()啦!!!
一. 最小栈
1.1 问题描述
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。 pop() —— 删除栈顶的元素。 top() —— 获取栈顶元素。 getMin() —— 检索栈中的最小元素。
1.2 思路及复杂度分析
最小栈问题的核心在于常数时间内检索最小元素
,也就是说我们需要一个内存空间实时的保存这个最小值。那这个内存空间用什么数据结构实现呢?
显然,声明一个最小值的全局变量是可以的,有着自动移动栈顶指针特性的栈同样可以,我们这里只介绍使用栈的思路,它的具体实现是这样的:
在压栈的时候,将要压栈的元素与最小值栈的元素比较,小于的话就更新最小栈。需要得到最小值时,只需要peek()弹出最小值栈的栈顶元素即可。看起来的确很easy的样子咯,如果你也这样认为的话,那就有继续往下看的必要——
假如有这样的情景,我们首先删除了数据栈的栈顶元素,这时最小值栈的栈顶元素也需要同步更新,但是我根本没有保存上个最小值,这可怎么办呢~
一种通俗的解决方式是在压入最小值栈时,不要更新原有的最小值,而是作为栈顶元素插入
,这样固然可以解决问题,甚至性能也完全看得过去,但能不能只使用一个栈就能同时存储最小值和数据
呢?
这就需要使用到Node啦。我们首先看下java源码中是如何应用Node的——
同样,这里我们可以类比key,Value的形式
将数据与最小值作为Node类的属性:
private static class Node{
private int val;
private int min;
public Node(int val,int min){
this.val = val;
this.min = min;
}
}
使用一个栈的解法,因为每一个最小值都会被存储导致性能略低于使用辅助栈的方式,但其代码可读性高,还是一种很值得的尝试。
再来简要看下两种解法的复杂度分析:
辅助栈
:时间复杂度O(1).空间复杂度O(N)一个栈
:时间复杂度O(1),空间复杂度O(N),但因为申请一个栈空间比申请多个Node节点对的空间要少,所以空间复杂度相对辅助栈较高。
1.3 趣味图解
辅助栈
一个栈
请移步力扣题解留言区:leetcode-cn.com/problems/mi…
1.4 代码演示
/**
*最小栈的解法
*、
class MinStack {
private Stack<Node> stack;
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<>();
}
public void push(int x) {
if(stack.empty())
stack.push(new Node(x, x));
else
stack.push(new Node(x, Math.min(x,stack.peek().min)));
}
public void pop() {
stack.pop();
}
public int top(){
return stack.peek().val;
}
public Integer getMin(){
return stack.peek().min;
}
private static class Node{
private int val;
private int min;
public Node(int val,int min){
this.val = val;
this.min = min;
}
}
}
/**
*辅助栈的解法
*/
class MinStack {
//数据栈
Stack<Integer> dataStack = null;
//最小值栈
Stack<Integer> minStack = null;
/** initialize your data structure here. */
public MinStack() {
dataStack = new Stack<Integer>();
minStack = new Stack<Integer>();
}
public void push(int x) {
dataStack.push(x);
if(minStack.isEmpty() || x<=minStack.peek())
minStack.push(x);
}
public void pop() {
if(dataStack.pop().equals(minStack.peek())){
minStack.pop();
}
}
public int top() {
return dataStack.peek();
}
public Integer getMin(){
if(!minStack.isEmpty())
return minStack.peek();
//throw new EmptyStackException();
return null;
}
}
日拱一卒,功不唐捐。