最近因為一些原因尤辱,我需要寫一個簡易的計算器考余,需要支持加減乘除,小數(shù)和負(fù)數(shù)運(yùn)算先嬉。其實思路很簡單,無非就是使用雙棧解析一串算術(shù)表達(dá)式的文本楚堤∫呗《算法》一書中也有一個簡單的demo含懊。但如何快速高效的解析,便成了一個問題衅胀。
百度了一圈岔乔,發(fā)現(xiàn)很多文章要先把表達(dá)式轉(zhuǎn)成后綴表達(dá)式,再實現(xiàn)求值滚躯。例如java實現(xiàn)算術(shù)表達(dá)式求值雏门,但這么做真的有必要嗎?只是解析加減乘除需要這么多代碼嗎掸掏?我對此抱有疑問茁影。
我的疑問在于,轉(zhuǎn)成后綴表達(dá)式無非就是對數(shù)字和操作符進(jìn)行入棧和出棧的操作丧凤,但這個操作不轉(zhuǎn)成后綴表達(dá)式也是可以的募闲。下面是我的代碼。
public static BigDecimal calculate(char[] array) {
Stack<String> opsStack = new Stack<>();//操作符棧
Stack<BigDecimal> valuesStack = new Stack<>();//值棧
String valueString = "";
int count=0;
//遍歷的時候進(jìn)行區(qū)分
for (char a : array) {
if (a == '-'&&count==0) {//為了能支持第一個數(shù)是負(fù)數(shù)進(jìn)行判斷
valueString+="-";
} else if (a == '+' || a == '*' || a == '/'||a=='-') {
opsStack.push(String.valueOf(a));
valuesStack.push(new BigDecimal(valueString));
valueString = "";
} else {
valueString += String.valueOf(a);
}
count++;
}
if (!valueString.equals("")) {
valuesStack.push(new BigDecimal(valueString));
}
int size = opsStack.size();
BigDecimal value = new BigDecimal(0);
String ops1 = "";
for (int i = 0; i < size; i++) {
BigDecimal tmp1 = valuesStack.pop();
BigDecimal tmp2 = valuesStack.pop();
String opsLast = opsStack.pop();//棧頂?shù)牟僮鞣? String afterLast = opsStack.isEmpty() ? "" : opsStack.peek();//倒數(shù)第二個操作符
/*
* 下面兩行代碼進(jìn)行優(yōu)先級判斷愿待,因為目前沒有括號浩螺,所以只用判斷最后一個和倒數(shù)第二個操作符的優(yōu)先級
*/
boolean isLastLow = opsLast.equals("+") || opsLast.equals("-");
boolean isAfterHighter = afterLast.equals("*") || afterLast.equals("/");
if (isLastLow && isAfterHighter) {
value = tmp1;
ops1 = opsLast;
valuesStack.push(tmp2);
} else {
BigDecimal tmpValue = new BigDecimal(0);
if (afterLast.equals("-")) {
tmp2 = new BigDecimal(-1).multiply(tmp2);
}
switch (opsLast) {
case "+":
tmpValue = tmp2.add(tmp1);
break;
case "-":
tmpValue = tmp2.subtract(tmp1);
break;
case "*":
tmpValue = tmp2.multiply(tmp1);
break;
case "/":
tmpValue = tmp2.divide(tmp1, 10, BigDecimal.ROUND_DOWN);
break;
}
if (!ops1.equals("")) {
switch (ops1) {
case "+":
case "-":
valuesStack.push(tmpValue.add(value));
break;
case "*":
valuesStack.push(tmpValue.multiply(value));
break;
case "/":
valuesStack.push(tmpValue.divide(value, 10, BigDecimal.ROUND_DOWN));
break;
}
ops1 = "";
} else {
valuesStack.push(tmpValue);
}
}
}
return valuesStack.pop();
}
下面是測試用例
因為比較懶,所以沒有進(jìn)行四舍五入之類的操作了仍侥。以后要支持其他運(yùn)算符例如log的話要出,只需要在優(yōu)先級那里加以判斷就行了。而加入需要支持括號的話访圃,則就會變的很復(fù)雜了厨幻。《算法》一書中的Dijkstra的雙棧算法很優(yōu)雅腿时,但局限性很大况脆,有幾個操作符就需要幾個括號。但我大概想了一下批糟,括號會存在嵌套的情況格了,需要由內(nèi)到外進(jìn)行操作,或許把最外層括號內(nèi)的包括括號存入操作符棧徽鼎,之后當(dāng)遍歷操作符棧遇到括號時候盛末,再把這個括號內(nèi)的數(shù)進(jìn)行之前的解析,而內(nèi)層括號里的數(shù)字和括號存入另一個操作符棧否淤,類似遞歸悄但。但這個寫起來太費(fèi)時間就不寫了。
歡迎大家對我的文章提出意見和建議