最近遇到一种类型的算法题,是实现栈的pop、push、peek等方法的同时,还让实现min取得栈中最小值的功能, 题目最特殊的一点就是,要求时间复杂度为O(1)。
具体的题目如下:
请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。
示例:MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/min-stack-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
很明显,这种题如果要保证其时间复杂度为O(1),就不能出现遍历操作,我们必须要在入栈的同时就要想办法保证获取到最小值。
后来知道了一种解法,就是通过单链表实现栈结构,给单链表增加一个结点用于存储每次入栈比较后的最小值。
既然知道了思路,我们把题解放到尾部 ,先来用单链表把栈结构实现。实现后也许你就明白解题方式了。
话不多说,直接上代码,先写一个简单的栈结构LinkedList,这里先不添加min结点
class LinkedList{
Object data;//保存每个入栈元素
LinkedList next;
public LinkedList(Object data, LinkedList next){
this.data = data;
this.next = next;
}
}
然后实现栈的一些常见操作,先弄个接口,统一定义一下栈的一些常见实现方法。
/**
* 定义栈接口
*/
public interface IStack<E> {
boolean isEmpty();//栈是否为空
boolean isFull();//栈是否已满
void push(E parm);//入栈
E pop();//删除栈顶元素
E peek();//取出栈顶元素
void display();//遍历打印栈数据
}
明确定义方法后,我们就来具体实现这些方法。
class OperateStack<E> implements IStack<E>{
//先初始化一个链表头结点,充当栈顶元素
LinkedList headNode = null;
@Override
public boolean isEmpty(){
if(headNode == null)//如果头结点为空就说明栈空
return true;
return false;
}
@Override
public boolean isFull(){
//单链表实现的栈,可以动态增加,暂时无需考虑长度问题
throw new RuntimeException("单链表暂时无需设置长度");
}
@Override
public void push(E parm){//这里需要好好理解下
if(headNode == null)
headNode = new LinkedList(parm,null);//第一个结点的next结点为null
else //这一步可以理解为逆序的单链表
headNode = new LinkedList(parm,headNode);//将nextNode设置为之前的那个结点
}
@Override
public E pop(){
LinkedList temp = headNode;//将最前面的一个元素保存下来用于返回
headNode = headNode.next;//移除最前面的一个元素
return (E) temp.data;
}
@Override
public E peek(){
return (E) headNode.data;//直接返回最前面的元素即可
}
@Override
public void display(){
LinkedList temp = headNode;//这里注意不能直接操作headNode,不然就无法继续其它操作了
while(temp != null){
System.out.print(temp.data+"\t");
temp = temp.next;
}
System.out.println("");
}
}
基本的几个操作实现后,我们写个测试类来测试下吧。
public class Test{
public static void main(String[] args){
//实例化栈操作类
OperateStack<Integer> op = new OperateStack<>();
//栈中添加一些元素
op.push(10);
op.push(15);
op.push(20);
op.push(25);
op.push(30);
//打印栈
op.display();
//获取栈顶元素
System.out.println("当前栈顶元素为:"+op.peek());
//取出栈顶元素
System.out.println("当前栈顶元素为:"+op.pop());
//再次打印当前的栈结构,看看栈顶元素是否已被取出
op.display();
}
}
测试结果如下图:
通过上面的操作,我们知道怎样通过单链表来实现一个栈结构了,现在回到那道题,解决起来就很容易了吧。
我们只需要对上面的单链表添加一个额外的结点变量min用于保存当前的最小值即可,然后根据题意补全实现对应的push、pop、min方法即可。
class MinStack {
LinkedList headNode = null;
/** initialize your data structure here. */
public MinStack() {
}
public void push(int x) {
if(headNode == null){
headNode = new LinkedList(x,x,null);
}else{//每次入栈比较并更新当前最小值
headNode = new LinkedList(x,Math.min(x,headNode.min),headNode);
}
}
public void pop() {
headNode = headNode.next;
}
public int top() {
return headNode.data;
}
public int getMin() {//经过push的比较,栈顶的最小值即为最小元素
return headNode.min;
}
}
class LinkedList{
int data;//保存每个入栈元素
int min;//保存当前元素以及栈中元素中的最小值
LinkedList next;
public LinkedList(int data, int min, LinkedList next){
this.data = data;
this.min = min;
this.next = next;
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
注:这题的解题思路也是来源于题目评论区,侵删。