第一章 栈和队列
1.1 设计一个有 getMin 功能的栈
【题目】
实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
【要求】
- pop、push、getMin 操作的时间复杂度都是 O(1)。
- 设计的栈类型可以使用现成的栈结构。
【难度】
士 ★☆☆☆
【题解】
数据结构采用两个栈:一个用来保存当前栈中的元素,记为 stackData;另一个栈用于保存每一步操作后栈中元素的最小值,记为 stackMin。实现方式有两种:
第一种方案:
- 压入数据规则:(假设当前数据为 newNum,先将其压入 stackData,然后判断 stackMin 是否为空)
- 如果为空,则 newNum 也压入 stackMin;
- 如果不为空,则比较 newNum 和 stackMin 的栈顶元素 topNum 的大小:
- 如果 newNum ≤ topNum,则 newNum 也压入 stackMin;
- 如果 newNum > topNum,则 newNum 不压入 stackMin。
- 弹出数据规则:
- 先在 stackData 中弹出栈顶元素,记为 value。然后比较其与当前 stackMin 的栈顶元素 topNum 的大小。
- 由压入规则可知,stackMin 中的元素从栈底到栈顶逐渐减小,topNum 既是 stackMin 的最小值,也是当前 stackData 中元素的最小值。因此,value 只可能大于或等于 topNum。
- 当 value = topNum,stackMin 弹出栈顶元素;
- 当 value > topNum,stackMin 不弹出栈顶元素。
- 压入操作和弹出操作是对应的。
- 查询当前栈中的最小值:
- 由压入规则和弹出规则可知,stackMin 始终记录着 stackData 中的最小值。所以,最小值即为 stackMin 的栈顶元素。
第二种方案:
- 压入数据规则:(假设当前数据为 newNum,先将其压入 stackData,然后判断 stackMin 是否为空)
- 如果为空,则 newNum 也压入 stackMin;
- 如果不为空,则比较 newNum 和 stackMin 的栈顶元素 topNum 的大小:
- 如果 newNum ≤ topNum,则 newNum 也压入 stackMin;
- 如果 newNum > topNum,则将 topNum 重复压入 stackdsadaMin。
- 弹出数据规则:
- 在 stackData 中弹出数据,同时弹出 stackMin 的栈顶元素。
- 压入操作和弹出操作是对应的。
- 查询当前栈中的最小值:
- 由压入规则和弹出规则可知,stackMin 始终记录着 stackData 中的最小值。所以,最小值即为 stackMin 的栈顶元素。
【分析】
相同点:
- 方案一和方案二都是用 stackMin 保存着 stackData 每一步的最小值;
- 所有操作的时间复杂度都是 O(1),空间复杂度都是 O(n)。
不同点:
- 方案一中 stackMin 压入时稍省空间,但是弹出时稍费时间;
- 方案二中 stackMin 压入时稍费空间,但是弹出时稍省时间。
【实现】
- MyStack.java
public interface MyStack {
/**
* 入栈
*
* @param newNum 压入堆栈
* @throws Exception 栈为空
*/
void push(int newNum) throws Exception;
/**
* 出栈
*
* @return 弹出堆栈
* @throws Exception 栈为空
*/
int pop() throws Exception;
/**
* 获得栈中最小元素
*
* @return 栈中最小元素
* @throws Exception 栈为空
*/
int getMin() throws Exception;
}
- AbstractMyStack.java
import java.util.Stack;
public abstract class AbstractMyStack implements MyStack {
protected Stack<Integer> stackData;
protected Stack