功能需求:
實現一個具有特殊功能的棧,首先要具有棧所要具備的功能,再實現此棧執行後能夠返回棧中最小值的操作。其中pop、push、getMin的操作複雜度都是O(l)。有必要時可以使用現成的站結構操作來設計棧類型。(pop:彈出棧頂元素、push:壓棧、getMin獲取最小棧元素)
詳細解析:
由於要操作一組數據,並且存入棧中,那麼我們首先考慮到的是棧就需要分塊,或者我們可以使用兩個棧來存儲,相比較操作複雜度,我們選用兩個棧來操作,這樣操作簡便。其中一個棧用來保存當前棧中的元素,其 作用就是用來保存數據壓棧處理,所以第一個棧可以記作stackData;另一個棧用來保存每彈出一個元素後獲取到的最小值保存的棧,所以可以記爲stackMin。首先我們可以選擇一種複雜但是理解簡單的方法來實現。
【方法一】壓棧數據規則:假設當前存儲棧彈出的棧元素爲newNum,將其壓入stackData,然後判斷第二個保存棧stackMin是否爲空:1.如果爲空,將當前的newNum也壓入stackMin;2.如果不爲空,則比較newNum和stackMin中的棧頂元素哪一個更小;3如果newNum更小或者兩個相等,則將newNum也壓入stackMin;4.如果stackMin棧頂元素小,則stackMin不壓入任何內容。
例如:我們可以壓入一組數據4,5,6,4,2,3,2的過程,其操作如下圖:
數據彈出規則:現在stackData中彈出棧頂元素,記作value。然後比較當前stackMin的棧頂元素和value值哪一個小。通過上面所說的壓棧規則可以得出stackMin中存在的最小元素是從棧底到棧頂逐漸變小的,stackMin棧頂元素既是stackMin中的最小值也是當前stackMin中的最小值。所以不會出現value比stackMin的棧頂元素更小的狀況,value只可能大於或者等於stackMin中棧頂元素。當value等於stackMin的棧頂元素時,stackMin彈出棧頂元素;當value大於stackMin的棧頂元素時,stackMin不彈出棧頂元素,返回value元素。這與棧的壓入與彈出規則是對應的。
查詢當前棧中元素最小值操作: 由壓入數據規則和彈出站數據規則,stackMin始終記錄這stackData中最小值,所以stackMin中的棧頂始終保存着當前stackData中的最小值。
【方法二】壓入數據規則:假設當前數據爲newNum,先將其壓入stackData,然後判斷stackMin中是否爲空,如果爲空,則newNum也將壓入stackMin;如果不爲空,則比較stackMin棧頂值和newNum中的哪一個值更小:1.如果newNum更小或者兩個數值相等則newNum也壓入stackMin,如果stackMin中棧頂元素小,則stackMin中的棧頂元素重複壓入stackMin,既在棧頂元素之上再壓入一個棧頂元素。還是上面那個例子,壓入一組數據4,5,6,4,2,3,2的過程,如圖
代碼實現:
方法一:
package com.hekaikai666.test1;
import java.util.Stack;
/**
*
* @author hekaikai666
* @time 2018年8月22日下午8:29:29
**/
public class GetMin {
// 定義兩個棧分別表示這兩組數據
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
/**
* 構造方法
*/
public GetMin() {
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
/**
* 壓棧的方法
*
* @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 {
this.stackData.push(newNum);
}
}
/**
* 彈出棧的方法
*
* @return int 返回此棧中的最小值
*/
public int pop() {
// 判斷棧中是否爲空
if (this.stackData.isEmpty()) {
throw new RuntimeException("Your Stack is Empty~");
}
// 獲取數據棧中彈出棧頂的數據
int value = this.stackData.pop();
// 數據如果相等,繼續彈出
if (value == this.getMin()) {
this.stackMin.pop();
}
// 返回value
return value;
}
/**
* 判斷空棧的方法並且返回此棧頂值
*
* @return int 棧頂值
*/
public int getMin() {
if (this.stackMin.isEmpty()) {
throw new RuntimeException("Your Stack is Empty~");
}
// 返回棧頂的值
return this.stackMin.peek();
}
}
方法二:
package com.hekaikai666.test1;
import java.util.Stack;
/**
*
* @author hekaikai666
* @time 2018年8月22日下午9:00:51
**/
public class GetMin2 {
// 定義兩個棧分別表示這兩組數據
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
/**
* 構造方法
*/
public GetMin2() {
this.stackData = new Stack<Integer>();
this.stackMin = new Stack<Integer>();
}
/**
* 壓棧存儲的方法
*
* @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);
}
/**
* 彈出棧的方法
*
* @return
*/
public int pop() {
// 判斷數據棧是否還有數據
if (this.stackData.isEmpty()) {
throw new RuntimeException("Your Stack is Empty~");
}
// 存入棧彈出
this.stackMin.pop();
// 返回彈出的數據
return this.stackData.pop();
}
/**
* 判斷空棧的方法並且返回此棧頂值
*
* @return int 棧頂值
*/
public int getMin() {
// 判斷存入棧是否爲空
if (this.stackMin.isEmpty()) {
throw new RuntimeException("Your Stack is Empty~");
}
// 返回棧頂的值
return this.stackMin.peek();
}
}
總結分析:
兩種方法都是用了兩個棧來整理存儲數據,方案二比方案一的好在存入棧中數據和數據棧中的數據基本保持一致,把不合法的數據全部保持如合適的數據。都是用stackMin棧保存着stackData每一步的最小值。共同點是操作的複雜度相同都相當於一次遍歷O(l),時間複雜度也都爲O(n)方案一壓棧省空間,但是彈出耗時間,方案二壓棧費空間,但是彈出操作省時間。