一,問題描述
實現一個棧(元素遵守先入后出順序),能夠通過 min 方法在 O(1)時間內獲取棧中的最小元素。同時,棧的基本操作:入棧(Push)、出棧(Pop),也是在O(1)時間內完成的。
二,問題分析
之所以認為這個問題有趣,是因為在實現 min 方法的過程 牽涉到了 “緩存一致性”問題。是不是聽起來很高大上?哈哈,我臆想的。
思路1:添加一個成員變量總是保存當前棧中最小的元素。該思路的實現代碼大致是這樣的:
public class MinStack { private LinkedList<Integer> stack; private int min;// save minimum ele of stack public int pop(){ //check pop minimum ele? } public void push(int ele){ //check push minimum ele? } public int getMin(){ return min; } }
這里就會存在一個問題:保存最小元素的 min 屬性 與 棧中的最小元素不一致。
比如:當從棧中 pop 最小元素時,那 min 屬性就要 保存 次最小元素了。那如何 找到次最小元素,然后賦值給 min 呢?
因此,問題的關鍵就是:當只使用一個 min 屬性時,如何保證 min 屬性 總是 保存的是當前棧中最小的元素?---即: min 代表的最小元素 要與 棧中的最小元素保存一致。一種方式是當pop出最小元素之后,再遍歷棧找出次最小的元素,並將之賦值給 min 。但是,由於遍歷使得時間復雜度不再是O(1)
思路2:
使用一個輔助棧。此方法能夠實現在O(1)時間內獲取棧中最小的元素,但是缺點是空間復雜度為O(N)
現在有兩個棧:一個是保存元素的數據棧,另一個是輔助棧,輔助棧的棧頂總是 當前數據棧中最小的元素。當Push元素時,首先將該元素Push到數據棧,然后再將該元素與輔助棧的棧頂元素比較:如果該元素比輔助棧的棧頂元素小,則將該元素Push到輔助棧中;否則將輔助棧的棧頂元素再Push到輔助棧中。
比如,現在要Push的元素順序如下:3,4,2,5.... 在數據棧 和 輔助棧中保存的元素如下:
三,代碼實現
代碼中使用了 java.util.LinkedList 類作為 棧的實現。
import java.util.LinkedList; public class MinStack { private LinkedList<Integer> dataStack; private LinkedList<Integer> minStack; public MinStack() { dataStack = new LinkedList<Integer>(); minStack = new LinkedList<Integer>(); } //base operation public void push(int ele) { dataStack.push(ele); if(minStack.size() == 0 || ele < minStack.peek()) minStack.push(ele); else minStack.push(minStack.peek()); } public Integer pop(){ if(dataStack.isEmpty()) return null; assert dataStack.isEmpty() == false && minStack.isEmpty() == false; int ele = dataStack.pop(); minStack.pop(); return ele; } public Integer min(){ if(minStack.isEmpty()) return null; return minStack.peek(); } //hapjin test public static void main(String[] args) { MinStack stack = new MinStack(); int[] eles = {3,4,2,5}; for (int i : eles) { stack.push(i); } System.out.println(stack.min());//2 System.out.println(stack.pop());//5 System.out.println(stack.pop());//2 System.out.println(stack.min());//3 stack.push(1); System.out.println(stack.min()); } }