// 枚舉, 對應加減乘除
public enum OperatorEum {
ADD("+"),
SUBSTRACT("-"),
MULTIPY("*"),
DIVIDE("/");
private String operatorSign;
private OperatorEum(String operatorSign) {
this.operatorSign = operatorSign;
}
public String getOperatorSign(){
return operatorSign;
}
public static OperatorEum findOperatorEnum(String operatorSign) {
for(OperatorEum value : values()) {
if(value.getOperatorSign().equals(operatorSign)) {
return value;
}
}
return null;
}
}
import java.util.Arrays;
// 定義一個棧,用來保存用於計算的數字和加減乘除符號
// 主要提供pop, peek, empty, size方法
public class CustomStack<T> {
private int capacity;
private int size = 0;
private static int MAX = 16;
private Object[] dataArr;
public CustomStack() {
this.capacity = MAX;
dataArr = new Object[this.capacity];
}
public CustomStack(int capacity) {
this.capacity = capacity;
dataArr = new Object[this.capacity];
}
public boolean push(T data) {
if(dataArr.length == size){
return false;
}
dataArr[size++] = data;
return true;
}
public T pop() {
T data = (T)dataArr[--size];
dataArr[size] = null;
return data;
}
public T peek(){
return (T)dataArr[size-1];
}
public boolean empty(){
return size == 0;
}
public int size() { return size;}
@Override
public String toString() {
return "CustomStack{" +
"dataArr=" + Arrays.toString(dataArr) +
'}';
}
public static void main(String[] args) {
}
}
package test.stack;
import java.util.ArrayList;
import java.util.List;
public class CustomMath {
// 保存在計算過程要用於計算的數字和臨時計算結果
private CustomStack<Integer> numberStack;
// 保存在計算過程中要用於計算的運算符號
private CustomStack<OperatorEum> operatorStack;
// 按順序保存數學式子中所有的數字
private List<Integer> numberList;
// 按順序保存數學式子中所有的運算符號
private List<OperatorEum> operatorEumList;
public CustomMath(){
numberStack = new CustomStack<>(10);
operatorStack = new CustomStack<>(10);
numberList = new ArrayList<>();
operatorEumList = new ArrayList<>();
}
// 正式進行計算
public Integer calculate(String mathExpression) throws Exception {
System.out.println("mathExpression: "+mathExpression);
Integer result = 0;
if(checkExpression()){
extractNumberAndOperatorToList(mathExpression);
int numberSize = numberList.size();
numberStack.push(numberList.get(0));
for(int i=1; i<numberSize; i++){
Integer number = numberList.get(i);
int j=i-1;
OperatorEum operatorEum = operatorEumList.get(j);
if(operatorStack.size() == 0){
numberStack.push(number);
operatorStack.push(operatorEum);
}else{
OperatorEum storedOperatorEum = operatorStack.peek();
if(storedOperatorEum.equals(operatorEum)){
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
result = calculate(number2, number1, operatorStack.pop());
numberStack.push(result);
numberStack.push(number);
operatorStack.push(operatorEum);
}else {
boolean superior = isSuperiorToStackOperator(storedOperatorEum, operatorEum);
if (superior) {
result = calculate(numberStack.pop(), number, operatorEum);
numberStack.push(result);
} else {
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
result = calculate(number2, number1, operatorStack.pop());
numberStack.push(result);
numberStack.push(number);
operatorStack.push(operatorEum);
}
}
}
}
}else{
throw new Exception("Illegal math expression");
}
while (numberStack.size() > 1){
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
result = calculate(number2,number1, operatorStack.pop());
numberStack.push(result);
}
return numberStack.pop();
}
// 具體的某種計算, 思路 number1 運算符號 number2, 兩個參數的位置很重要,決定了
// 在計算式了中的位置,直接影響計算結果,比如3-2與2-3,
private Integer calculate(Integer number1, Integer number2, OperatorEum operatorEum){
Integer result = 0;
switch (operatorEum) {
case ADD:
result = number1+number2;
break;
case SUBSTRACT:
result = number1-number2;
break;
case MULTIPY:
result = number1*number2;
break;
case DIVIDE:
result = number1/number2;
break;
}
return result;
}
// 將數學式子中的數字和運算符號按順序保存到對應的List中
private void extractNumberAndOperatorToList(String mathExpression){
String numberStr = "";
int length = mathExpression.length();
for(int i=0; i<length; i++){
char ch = mathExpression.charAt(i);
OperatorEum operatorEum = OperatorEum.findOperatorEnum(String.valueOf(ch));
if(operatorEum == null) {
numberStr += ch;
}else{
this.numberList.add(Integer.valueOf(numberStr));
numberStr = "";
this.operatorEumList.add(operatorEum);
}
}
this.numberList.add(Integer.valueOf(numberStr));
}
// 一般性的校驗,檢查一下輸入的數學式子是否合未能
private boolean checkExpression(){
return true;
}
// 比較後一個運算符號的級別是否高於前一個運算符號,參數的位置很重要,
// 前一個運算符號要作爲第一個參數, 後一個運算符號要作爲第二個參數
private boolean isSuperiorToStackOperator(OperatorEum operatorEum1, OperatorEum operatorEum2){
String stackOperator = operatorEum1.getOperatorSign();
String operator = operatorEum2.getOperatorSign();
boolean highPriority = operator.equals("*") || operator.equals("/");
boolean lowPriority = stackOperator.equals("+") || stackOperator.equals("-");
return highPriority && lowPriority;
}
// 進行測試
public static void main(String[] args) {
String expression = "0-1/1+2*2/2-1"; //
CustomMath customMath = new CustomMath();
try {
Integer result = customMath.calculate(expression);
System.out.println(result);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
核心思路講解:
1. numberStack中只保存兩個數字, operatorStack中只保存一個運算符號,這樣兩個棧中數據合起來就是一個完整的數學表達式: a+b,兩個棧中的數據一直維持這種狀態。 2.初次入棧。在第一次執行numberList的遍歷前,先在numberStack中放入一個數字numberStack.push(numberList.get(0)),然後從1開始遍歷。經過下面步驟,兩個棧中的數據滿足了第1點要求。
if(operatorStack.size() == 0){
numberStack.push(number);
operatorStack.push(operatorEum);
}
3. 遍歷計算及入棧講解,看代碼按思路分析。
// storedOperatorEum爲符號棧中保存的運算符號, operatorEum爲從符號list取出的運算符號
// number是從數字list中取出的數字,將用來計算或是入棧
OperatorEum storedOperatorEum = operatorStack.peek();
if(storedOperatorEum.equals(operatorEum)){
//如果運算符號相同,先將棧中數字和運算符號彈出並進行計算,將計算結果入棧保存,再將number入棧保存,
// 將operatorEum入棧保存
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
// 此處要注意,一定是第二次彈出的數字作爲第一個運算數字,第一次彈出的數字作爲第二個運算數字
// 因爲3-2與2-3的結果就完全不同
result = calculate(number2, number1, operatorStack.pop());
numberStack.push(result);
numberStack.push(number);
operatorStack.push(operatorEum);
}else {
boolean superior = isSuperiorToStackOperator(storedOperatorEum, operatorEum);
if (superior) {
// operatorEum的級別高於棧中運算符號的級別,就要數字棧中頂元素與number進行運算
// 將計算結果入棧。 要注意的是數字棧中的頂元素要作爲第一個運算數字, number是作爲第二個
// 此時,operatorEum與number都完成了計算,所以不需要入棧了
result = calculate(numberStack.pop(), number, operatorEum);
numberStack.push(result);
} else {
// 如果operatorEum的級別與棧中運算符號的級別相等,則將棧中數字和運算符號彈出並進行計算,將計算// 結果入棧保存。
// 然後number與operatorEum要進行入棧
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
result = calculate(number2, number1, operatorStack.pop());
numberStack.push(result);
numberStack.push(number);
operatorStack.push(operatorEum);
}
}