一、题目描述
实现一个特殊的栈结构,该栈在具有一般栈的结构的基础上还具有获取栈最小元素的功能。
二、题目要求
1.栈的pop、push、getMin操作的时间复杂度均为O(1);
2.设计特殊栈的时候可以使用现成的栈作为工具去完成。
三、题目解析
题目要求提醒我们可以使用现成的栈的数据结构,其实也就是告诉我们要使用其作为工具去构建我们的特殊的栈,因为其getMin操作是O(1),因此告诉我们其操作肯定不是在自己的栈里瞎折腾,因此我们需要两个栈,一个用来存储基本数据,另一个用来存储getMin操作得到的数字。此时假设我们存储数据的栈的名字为stackData,存储getMin数字的栈为stackMin。
此处提供的实现方式有两种,如下。
第一种方案:
对于stackData栈没有什么可以说的,就是对应特殊栈的pop、push使用一个普通栈去存储就可以了,问题的关键主要是子在push和pop的时候对于stackMin的操作。假设当前数据为newData,我们执行push操作时,先将其压入stackData种,然后判断stackMin是否为null,如果为null,则将其压入stackMin里,如果不为null,则比较newData和stackMin的栈顶元素谁小,如果newData小或者相等就将其压入stackMin中,否则则不压入;当执行pop方法时,stackData正常弹出栈顶元素,根据上面描述的push规则,stackMin的元素大小其实是按照从栈底到栈顶从小到大排列的,且其元素的个数是要小于等于stackData的,因此在执行pop操作时,需要比较要弹出的元素(记为data1)和栈顶元素大小进行比较,若data1比栈顶元素大,则直接返回stackMin的栈顶元素的值,并不弹出,否则则弹出;执行getMin操作就非常简单了,就直接返回stackMin栈顶元素即可。
第二种方案:
其实第二种方案的思想和第一种并没有什么不同,通过观察第一种方案的操作,其每次pop都要进行比较,此种方案就是构造一个可以直接pop的stackMin,其实就是在当newValue压入的时候,当newData的大小大于stackMin的栈顶元素时,同步压入一个stackMin的栈顶元素,这样就可以保证stackMin的元素数目和stackData保持一致,这样在pop时直接调用其原生的pop方法即可完成功能。
两种方案的比较:
其相同点为时间复杂度和空间复杂度都为O(1)和O(n),不同点是第一种在push的时候稍省空间但pop操作稍费时间,第二种在push的时候稍费空间,但是pop的时候稍省时间。
四、代码参考
import java.util.Stack;
public class MyStack1 {
/**设置一个具有getMin功能的栈
* 设置一个特殊的栈,在完成栈的基本功能的时候 然后再实现返回栈的最小元素的操作
* 要求:
* pop push getMin的操作都是O(1)的 设计栈的时候可以使用现成的栈结构
* --
* 在设计的时候 我们考虑设计两个栈 一个是正常的栈 其操作也正常记为stackData 另外一个栈用来记录每一步的最小值这个栈记为stackMin,实现方式有两种:
* */
/*
* 第一种设计方案:
* 1.压入规则
* 首先一个value来的时候 首先判断stackMin是否为null 若stackMin为null 则把value直接压入stackMin
* 当stackMin不为null的时候 value与stackMin的栈顶元素进行比较 若value比栈顶元素小或相等 则把value压入栈 如果value比栈顶元素大 则不压入任何值
* 2.弹出规则
* 弹出规则和压入规则相对应 我们可以知道 stackMin的栈顶元素就是当前stackData的最小值 所以当value弹出的时候和stackMin的栈顶元素进行比较
* 相等时 弹出stackMin的栈顶元素 当当前value大于stackMin的栈顶元素时 不弹出栈顶元素 直接返回value的值
* */
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
public MyStack1() {
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
public void push(int newNum) {
if(this.stackMin.isEmpty()) {
this.stackMin.push(newNum);
} else if(newNum <= this.getmin()) {
this.stackMin.push(newNum);
}
this.stackData.push(newNum);
}
public int pop() {
if(this.stackData.isEmpty()) {
throw new RuntimeException("your stack is empty!!");
}
int value = this.stackData.pop();
if(value == this.getmin()) {
this.stackMin.pop();
}
return value;
}
public int getmin() {
if(this.stackMin.isEmpty()) {
throw new RuntimeException("your stack is empty!!");
}
return this.stackMin.peek();
}
}
class MysStack2 {
/**
* 第二种方案:
* 其实第二种方案的不同的地方就是在压入的时候 当value的值比栈顶元素大的时候 压入一个和原来栈顶元素一样的值
* 当弹出的时候 弹出一个值stackMin栈就从弹出一个栈顶元素
* 比较:第一种方案在压入数据的时候比较省空间 第二中方案在弹出的时候比较省时间
* 但是其时间复杂度O(1)和空间复杂度都是O(n)
* */
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
public void push(int newValue) {
if(this.stackMin.isEmpty()) {
this.stackMin.push(newValue);
} else if(newValue < this.getmin()) {
this.stackMin.push(newValue);
} else {
int newMin = this.stackMin.peek();
this.stackMin.push(newMin);
}
}
public int pop() {
if(this.stackData.isEmpty()) {
throw new RuntimeException("your stack is empty!!");
}
this.stackMin.pop();
return this.stackData.pop();
}
public int getmin() {
if(this.stackMin.isEmpty()) {
throw new RuntimeException("your stack is empty!!");
}
return this.stackMin.peek();
}