命题
实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作
要求:
- pop、push、getMin操作的时间复杂度都是O(1);
- 设计的栈类型可以使用现成的栈结构。
难度 ♥(压力好大...)
设计思路:
两个栈-普通栈+getMin栈,它的主要目的还是要获取到stack对象的最小值,如果两个栈同为最小值也一并被pop()方法弹出(实现弹栈的基本功能罢了)
方案一
让较大的数不压入最小值的栈中,stackMin不做任何操作继续保持原样,这样即可通过栈对象的peek()方法返回最小值
具体实现
package com.demo.mystack;
import java.util.Stack;
/**
* 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作
* 要求:
* 1、pop、push、getMin操作的时间复杂度都是O(1)
* 2、设计的栈类型可以使用现成的栈结构
*/
/**
* @author lorem
* @date 2018/8/19
*/
public class MyStack {
/**
* stackData 基本信息栈
* stackMin 最小栈
**/
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
@Test
public void test(){
MyStack myStack = new MyStack();
myStack.push(2);
myStack.push(5);
myStack.push(4);
myStack.push(1);
System.out.println(myStack.pop());
System.out.println(myStack.getmin());
MyStack myStack1 = new MyStack();
myStack1.pop();
}
/**
* 通过构造方法,实例化两个Stack对象 stackData、stackMin
*/
public MyStack(){
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
/**
* push方法将元素压栈
*/
private 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);
}
/**
* stackData.pop()返回值赋值给value
* value比对最小值,如果相等则将最小栈中的元素也弹出
* @return 返回value
*/
private int pop(){
//因为最大值会先入stackData栈中所以只要判断此是否为空即可
if (this.stackData.isEmpty()){
throw new RuntimeException("your stack is empty");
}
int value = this.stackData.pop();
// 这样的判断也是为了避免NPE
if (value==this.getmin()){
this.stackMin.pop();
}
return value;
}
/**
*getmin 获取最小元素
* 利用peek方法,返回的元素并不会删除
*
* @return 最小栈元素
*/
private int getmin(){
if (this.stackMin.isEmpty()){
throw new RuntimeException("your stack is emtry");
}
return this.stackMin.peek();
}
}
方案二
在遇到较大的值时,压入上一次重复的较小值
具体实现
package com.demo.mystack;
import org.junit.Test;
import java.util.Stack;
/**
* @author lorem
* @date 2018/8/19
*/
public class MyStack1 {
/**
* stackData 基本信息栈
* stackMin 最小栈
*/
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
/**
* 通过初始化构造函数创建两个相关对象
*/
public MyStack1(){
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
@Test
public void test(){
MyStack1 myStack1 = new MyStack1();
myStack1.push(2);
myStack1.push(5);
myStack1.push(4);
myStack1.push(1);
System.out.println(myStack1.pop());
System.out.println(myStack1.getmin());
}
/**
*
* @param newNum
*
*/
public void push(int newNum){
if (this.stackMin.isEmpty()){
this.stackMin.push(newNum);
}else if (newNum < this.getmin()){
this.stackMin.push(newNum);
}else {
int newMin = this.stackMin.peek();
this.stackMin.push(newMin);
}
this.stackData.push(newNum);
}
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 emtry");
}
return this.stackMin.peek();
}
}
总结
两种方案的最终目的都是让较大的数不压入最小值的栈中,方案一压栈时稍省空间,弹栈时稍费空间;方案二则压栈时费空间,弹栈时稍省空间。
因为方案一的实现在遇到较大值时,不会压入任何数据保持原样,所以在弹栈时需要做一个额外的判断,以免出现NPE。
int value = this.stackData.pop();
// 这样的判断也是为了避免NPE
if (value==this.getmin()){
this.stackMin.pop();
}
而方案二stackData与stackMin两个栈空间容量是等同的,所以,在弹栈时则比较顺便了
this.stackMin.pop();
this.stackData.pop();
也就是说,如果只是进行压栈操作而对弹栈操作较少的情况下用方案一;如果对弹栈操作较为频繁就用方案二。