一、题目和要求
【题目】
实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
【要求】
1.pop、push、getMin 操作的时间复杂度都是O(1)。
2.设计的栈类型可以使用现成的栈结构。
二、思路分析
要实现能够查看栈中的最小值,我们需要记录下当前栈中数据的最小的数据,因为栈是涉及到出栈操作的,如果仅用一个值来记录最小值,当栈中的元素出栈以后,那个最小值就有可能失效,所以我首先想到的是在getMin方法中将所有的栈遍历一遍,但是栈没有遍历的方法,如果要遍历就需要用一个新的栈进行辅助,而且在这种方法需要遍历栈中的元素,不满足复杂度是O(1)的要求。
另一种思路就是除了保存数据的栈,我们再新建一个保存最小值的栈,在这个栈中对应于数据栈保存对应的栈中的最小值,这样就可以轻松的获得栈中的最小值,而且也满足复杂度O(1)的要求
三、实现方案
方案一:
实现方法解析:
创建一个存放最小值的栈minStack,在入栈的时候,将push的数据和当前最小值栈minStack中的栈顶元素进行比较,如果minStack为空或者push的值小于等于栈顶元素,就将push的数据入栈,否则,就对应的放一个和minStack栈顶元素相同的值,用来和dataStack中的数据相对应,在出栈的时候,我们就同时将minStack和dataStack出栈,这样两个栈中的数量是相同的,可以达到效果。示例代码如下:
示例代码
- 首先我们创建一个抽象类AbstractSolution,用来编写不同方案相同的代码
public static abstract class AbstractSolution {
protected Stack<Integer> dataStack;
protected Stack<Integer> minStack;
public AbstractSolution() {
this.dataStack = new Stack<>();
this.minStack = new Stack<>();
}
public Integer getMin() {
if (minStack.isEmpty()) {
throw new RuntimeException("stack is empty. ");
}
return minStack.peek();
}
public boolean isEmpty() {
return this.dataStack.isEmpty();
}
public String getAllData() {
return dataStack.toString();
}
public String getAllMinData() {
return minStack.toString();
}
public abstract void push(Integer data);
public abstract Integer pop();
}
- 编写一个统一的测试方法,用来测试数据
private void testSolution(AbstractSolution solution) {
for (Integer integer : Arrays.asList(3, 4, 5, 1, 2, 1)) {
solution.push(integer);
}
while (!solution.isEmpty()) {
// 用栈的toString() 方法打印出来的栈是栈顶元素在最后面,它是按照入栈顺序打印的
System.out.println(
" ,currentData=" + solution.getAllData() + " -> minData= " + solution.getMin() + ",minStack="
+ solution.getAllMinData());
System.out.print("popData: " + solution.pop());
}
}
- 编写方案一的代码和测试方法
public static class Solution1 extends AbstractSolution {
public Solution1() {
super();
}
public void push(Integer data) {
Integer newMinData = minStack.isEmpty() ? data : data <= getMin() ? data : getMin();
this.minStack.push(newMinData);
this.dataStack.push(data);
}
public Integer pop() {
if (dataStack.isEmpty()) {
throw new RuntimeException("stack is empty. ");
}
this.minStack.pop();
return this.dataStack.pop();
}
}
- 测试方法:
@Test
public void testSolution1() {
testSolution(new Solution1());
}
测试结果
,currentData=[3, 4, 5, 1, 2, 1] -> minData= 1,minStack=[3, 3, 3, 1, 1, 1]
popData: 1 ,currentData=[3, 4, 5, 1, 2] -> minData= 1,minStack=[3, 3, 3, 1, 1]
popData: 2 ,currentData=[3, 4, 5, 1] -> minData= 1,minStack=[3, 3, 3, 1]
popData: 1 ,currentData=[3, 4, 5] -> minData= 3,minStack=[3, 3, 3]
popData: 5 ,currentData=[3, 4] -> minData= 3,minStack=[3, 3]
popData: 4 ,currentData=[3] -> minData= 3,minStack=[3]
popData: 3
方案二:
实现方法解析
方案一种,minStack中的数据量和dataStack中是相同的,虽然在复杂度上面能满足要求,但我们仍然可以想办法减少minStack中的数量,达到减少空间复杂度的目的,具体思路如下:
方案一中,我们在push的时候,如果push的数据大于minStack的栈顶元素,我们也是放了一个相同的栈顶元素进去,这样就造成了数据重复,我们可以从这里着手改进。如果在放入的时候,如果push的元素大于当前minStack中的栈顶元素,我们不放进去要怎么样呢?这样的结果就是两个栈的数量对不上,在pop元素的时候,就不能简单的将minStack中的元素也pop出来,需要将数据栈中pop出来的元素和当前minStack中的元素进行对比,从我们入minStack的逻辑可以看出,我们只放置了和push元素相等或者小于的值,也就是说,无论minStack是否为空,我们都会放入和push元素相同的值,这一点非常重要,从这个逻辑中我们可以发现,只要在出栈的时候,如果minStack中的栈顶元素和dataStack中的相同,我们就可以将minStack出栈一次,否则我们就不需要操作。代码如下:
示例代码
- 方案二的代码
public static class Solution2 extends AbstractSolution {
public Solution2() {
super();
}
public void push(Integer data) {
if (this.minStack.isEmpty()) {
this.minStack.push(data);
} else if (data.compareTo(getMin()) <= 0) {
this.minStack.push(data);
}
this.dataStack.push(data);
}
public Integer pop() {
if (dataStack.isEmpty()) {
throw new RuntimeException("stack is empty. ");
}
Integer data = dataStack.pop();
// 为何是等于的时候会出栈?因为我们入栈的时候,放了小于和等于的值
if (data.equals(getMin())) {
this.minStack.pop();
}
return data;
}
}
- 测试方法
@Test
public void testSolution2() {
testSolution(new Solution2());
}
测试结果:
,currentData=[3, 4, 5, 1, 2, 1] -> minData= 1,minStack=[3, 1, 1]
popData: 1 ,currentData=[3, 4, 5, 1, 2] -> minData= 1,minStack=[3, 1]
popData: 2 ,currentData=[3, 4, 5, 1] -> minData= 1,minStack=[3, 1]
popData: 1 ,currentData=[3, 4, 5] -> minData= 3,minStack=[3]
popData: 5 ,currentData=[3, 4] -> minData= 3,minStack=[3]
popData: 4 ,currentData=[3] -> minData= 3,minStack=[3]
popData: 3
四、总结
为了方便参考,整体代码也贴出来:
/**
* 【题目】
* 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
* 【要求】
* 1.pop、push、getMin 操作的时间复杂度都是O(1)。
* 2.设计的栈类型可以使用现成的栈结构。
*/
public class StackGetMin {
@Test
public void testSolution1() {
testSolution(new Solution1());
}
@Test
public void testSolution2() {
testSolution(new Solution2());
}
private void testSolution(AbstractSolution solution) {
for (Integer integer : Arrays.asList(3, 4, 5, 1, 2, 1)) {
solution.push(integer);
}
while (!solution.isEmpty()) {
// 用栈的toString() 方法打印出来的栈是栈顶元素在最后面,它是按照入栈顺序打印的
System.out.println(
" ,currentData=" + solution.getAllData() + " -> minData= " + solution.getMin() + ",minStack="
+ solution.getAllMinData());
System.out.print("popData: " + solution.pop());
}
}
public static class Solution1 extends AbstractSolution {
public Solution1() {
super();
}
public void push(Integer data) {
Integer newMinData = minStack.isEmpty() ? data : data <= getMin() ? data : getMin();
this.minStack.push(newMinData);
this.dataStack.push(data);
}
public Integer pop() {
if (dataStack.isEmpty()) {
throw new RuntimeException("stack is empty. ");
}
this.minStack.pop();
return this.dataStack.pop();
}
}
public static class Solution2 extends AbstractSolution {
public Solution2() {
super();
}
public void push(Integer data) {
if (this.minStack.isEmpty()) {
this.minStack.push(data);
} else if (data.compareTo(getMin()) <= 0) {
this.minStack.push(data);
}
this.dataStack.push(data);
}
public Integer pop() {
if (dataStack.isEmpty()) {
throw new RuntimeException("stack is empty. ");
}
Integer data = dataStack.pop();
// 为何是等于的时候会出栈?因为我们入栈的时候,放了小于和等于的值
if (data.equals(getMin())) {
this.minStack.pop();
}
return data;
}
}
public static abstract class AbstractSolution {
protected Stack<Integer> dataStack;
protected Stack<Integer> minStack;
public AbstractSolution() {
this.dataStack = new Stack<>();
this.minStack = new Stack<>();
}
public Integer getMin() {
if (minStack.isEmpty()) {
throw new RuntimeException("stack is empty. ");
}
return minStack.peek();
}
public boolean isEmpty() {
return this.dataStack.isEmpty();
}
public String getAllData() {
return dataStack.toString();
}
public String getAllMinData() {
return minStack.toString();
}
public abstract void push(Integer data);
public abstract Integer pop();
}
}
后记
个人总结,欢迎转载、评论、批评指正