最小栈

题目

设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈。

  • push(x) -- 将元素 x 推入栈中。
  • pop() -- 删除栈顶的元素。
  • top() -- 获取栈顶元素。
  • getMin() -- 检索栈中的最小元素。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

元素和最小值结对法

将「入栈元素」和「其入栈后栈内最小值」组成一对,入栈、出栈操作都是操作对。

举个例子,创建一个栈,初始为空,不存在最小值。

当压入值-2时,最小值变为-2。所以同时压入值-2和最小值-2

当压入值0,最小值依旧为-2。所以同时压入值0和最小值-2

当压入值-3时,最小值变为-3。所以同时压入值-3和最小值-3

将值和最小值结对压入栈,保证栈顶元素就是最小值。

当弹出元素时,将值和最小值一同弹出。之前最小值从-2变为-3是因为值-3的加入,现在-3被移出栈了,最小值也就回复到之前的-2

代码实现

package io.github.rscai.leetcode.bytedance.datastructure; import java.util.ArrayList; import java.util.List; public class Solution1049A { private List<Integer> stack; /** * initialize your data structure here. */ public Solution1049A() { stack = new ArrayList<>(); } public void push(int x) { int currentMin = Integer.MAX_VALUE; if (stack.size() > 0) { currentMin = stack.get(stack.size() - 1); } stack.add(x); if (x < currentMin) { stack.add(x); } else { stack.add(currentMin); } } public void pop() { if (stack.size() > 0) { stack.remove(stack.size() - 1); stack.remove(stack.size() - 1); } } public int top() { if (stack.size() > 0) { return stack.get(stack.size() - 2); } else { return Integer.MIN_VALUE; } } public int getMin() { if (stack.size() > 0) { return stack.get(stack.size() - 1); } else { return Integer.MIN_VALUE; } } }

使用JDK提供的ArrayList来实现有序列表,栈就只能在一端增加删除元素的有序列表。

debug-A1

入栈的时候将值和最小值成对入栈。最小值通过取之前最小值和当前压入值之中最小的值得到。

debug-A2

出栈的时候也成对出栈。

debug-A3

值和最小值是成对压入、弹出的,栈顶元素是最值,从顶往下第二个元素就是罗辑上的「栈顶」元素。

debug-A4

值和最小值是成对压入、弹出的,栈顶元素就是最小值。

debug-A5

复杂度分析

时间复杂度

push只在栈顶操作,且每次都是固定压入两个元素(值和最小值),跟栈中的数据量无关。所以时间复杂度为

pop也只在栈顶操作,且每次都是固定弹出两个元素(值和最小值),跟栈中数据量无关。所以时间复杂度为

top固定读取从顶往下第二个元素,跟栈中数据量无关。所以时间复杂度为

getMin固定读取栈顶元素,跟栈中数据量无关。所以时间复杂度为

空间复杂度

本结构将每个值都扩展为两个值组成的对,假设值的数据量为,则栈中将有个元素。空间复杂度为,消掉常量为

双栈法

创建两个栈,一个用于存储值,另一个用于存储最小值。

举个例子,创建两个栈dataStackminValueStack

把值-2压入dataStack时,最小值变为-2,将最小值-2同时压入minValueStack。

把值0压入dataStack时,最小值没有发生变化,所以就不压入minValueStack。

把值-3压入dataStack时,最小值变为-3,所以把-3同时压入minValueStack。

minValueStack栈顶元素就是最小值。

从dataStack弹出值时,要检查minValueStack栈顶元素是否等于dataStack栈顶元素,若是则表明「当前最小值」被移出了dataStack,minValueStack要相应地将其弹出。

代码实现

package io.github.rscai.leetcode.bytedance.datastructure; import java.util.ArrayList; import java.util.List; public class Solution1049B { private List<Integer> dataStack; private List<Integer> minValueStack; /** * initialize your data structure here. */ public Solution1049B() { dataStack = new ArrayList<>(); minValueStack = new ArrayList<>(); } public void push(int x) { int currentMin = Integer.MAX_VALUE; if (minValueStack.size() > 0) { currentMin = minValueStack.get(minValueStack.size() - 1); } dataStack.add(x); if (x <= currentMin) { minValueStack.add(x); } } public void pop() { if (dataStack.size() > 0) { int dataValue = dataStack.remove(dataStack.size() - 1); if (dataValue == minValueStack.get(minValueStack.size() - 1)) { minValueStack.remove(minValueStack.size() - 1); } } } public int top() { if (dataStack.size() > 0) { return dataStack.get(dataStack.size() - 1); } else { return Integer.MIN_VALUE; } } public int getMin() { if (minValueStack.size() > 0) { return minValueStack.get(minValueStack.size() - 1); } else { return Integer.MIN_VALUE; } } }

使用JDK提供的ArrayList实现两个栈,dataStack存储值,minValueStack存储最小值。

debug-B1

压入值时,先将值压入dataStack,再比较新压入值跟当前最小值。若新压入值小于或等于当前最小值,则最小值被改变,将新值同时压入minValueStack。

debug-B2

弹出值时,除了将dataStack栈顶元素弹出,同时若minValueStack栈顶元素等于dataStack弹出元素,则意味着minValueStack栈顶的最小值被移除了,所以要同步弹出minValueStack栈顶元素。

debug-B3

dataStack专门存储值,所以dataStack栈顶元素即即最后压入元素。

debug-B4

minValueStack专门存储最小值,minValueStack栈顶元素即最小值。

debug-B5

复复杂分析

时间复杂度

push操作最多在两个栈中分别压入一个元素,时间复杂度为

pop操作最多从两个栈中分别弹出栈顶元素,时间复杂度为

top操作直接从栈dataStack读取栈顶元素,时间复杂度为

getMin操作直接从栈minValueStack读取栈顶元素,时间复杂度为

空间复杂度

本结构使用两个栈,假设值的数据量为,则dataStack佔用个存储空间,minValueStack最坏情况下(所有值都相等或值以降序顺序压入)佔用个存储空间。总体空间复杂度为

results matching ""

    No results matching ""