表達式求值
求一個非負整數四則混合運算且含嵌套括號表達式的值。如:
# 輸入:
1+2*(6/2)-4
# 輸出:
3.0
數據保證:
- 保證表達式合法(含除數不爲0)。
- 保證運算數是非負整數。
雙棧版
維護兩個棧: 符號棧,數字棧,遍歷輸入串過程中計算
- 數字直接入棧
- 符號入棧
a. 符號棧爲空
b. 當前符號優先於棧頂符號
c. 棧頂爲’(’ - 符號出棧計算: 棧頂符號非’(’ 且 優先級更高.
class ExpStack {
private static final String LEVEL_OPTS = ")+-*/(";
public double solve(String input) {
Stack<Character> optStack = new Stack<>();
Stack<Double> numStack = new Stack<>();
int i = 0;
while (i < input.length()) {
if (Character.isDigit(input.charAt(i))) { // 如果是數字直接入棧
int t = 0;
for (; i < input.length() && Character.isDigit(input.charAt(i)); i++) {
t = t * 10 + input.charAt(i) - '0';
}
numStack.push((double) t);
} else {// 如果是操作符
//棧爲空 或 當前優先於棧頂: 入棧
if (optStack.isEmpty() || compPriority(input.charAt(i), optStack.peek()) > 0) {
optStack.push(input.charAt(i++));
} else if (optStack.peek() == '(') { // 棧頂爲左括號 '('
if (input.charAt(i) == ')') { // 括號完成: 彈出'('
optStack.pop();
} else { // 括號開始 : 入棧
optStack.push(input.charAt(i));
}
i++;
} else { // 棧頂優先級更高且非 '(' : 運算
double top = numStack.pop();
numStack.push(calc(numStack.pop(), optStack.pop(), top));
}
}
}
while (!optStack.isEmpty()) {
double top = numStack.pop();
numStack.push(calc(numStack.pop(), optStack.pop(), top));
}
return numStack.pop();
}
private int compPriority(char c1, char c2) {
return LEVEL_OPTS.indexOf(c1) - LEVEL_OPTS.indexOf(c2);
}
private double calc(double x, char o, double y) {
switch (o) {
case '+':
return x + y;
case '-':
return x - y;
case '*':
return x * y;
case '/':
return x / y;
}
return 0;
}
}
二叉樹版
構建二叉樹: 非葉子節點爲符號,葉子節點爲數字. 最終後序搜索計算
二分點: 表達式中最後一個計算的運算符
- 排除括號後
- 優先取 + | -
- 再考慮 * | /
class ExpTree {
private String mInput;
private java.util.LinkedList<Node> mTree;
public double solve(String input) {
mInput = input;
mTree = new java.util.LinkedList<>();
buildTree(0, mInput.length());
return dfs(mTree.size() - 1);
}
private int buildTree(int li, int ri) {
try { // 先嚐試吧表達式解析爲葉子節點(純運算數)
int n = Integer.parseInt(mInput.substring(li, ri));
Node node = new Node(n, -1, -1);
mTree.addLast(node);
return mTree.size() - 1;
} catch (Exception ignore) {
}
// 找到最外層的運算符(最後一個計算的運算符,優先級最低的符號)
int opt, as = -1, md = -1, bracket = 0;
for (int i = li; i < ri; i++) {
switch (mInput.charAt(i)) {
case '(':
bracket++;
break;
case ')':
bracket--;
break;
case '+':
case '-':
if (bracket == 0) {
as = i;
}
break;
case '*':
case '/':
if (bracket == 0) {
md = i;
}
break;
}
}
opt = as < 0 ? md : as;
if (opt < 0) { // 發現這是一個被括號包裹的表達式(去掉括號重新構造)
return buildTree(li + 1, ri - 1);
}
// 依次構造左右子樹
Node node = new Node(mInput.charAt(opt), buildTree(li, opt), buildTree(opt + 1, ri));
mTree.addLast(node);
return mTree.size() - 1;
}
private double dfs(int i) { // 後序遍歷求解
if (mTree.get(i).lch == -1 && mTree.get(i).rch == -1) {
return mTree.get(i).num;
}
switch (mTree.get(i).opt) {
case '+':
return dfs(mTree.get(i).lch) + dfs(mTree.get(i).rch);
case '-':
return dfs(mTree.get(i).lch) - dfs(mTree.get(i).rch);
case '*':
return dfs(mTree.get(i).lch) * dfs(mTree.get(i).rch);
case '/':
return dfs(mTree.get(i).lch) / dfs(mTree.get(i).rch);
}
return 0;
}
private static class Node {
double num;
char opt;
int lch, rch;
Node(double n, int l, int r) {
num = n;
initChild(l, r);
}
Node(char o, int l, int r) {
opt = o;
initChild(l, r);
}
private void initChild(int l, int r) {
lch = l;
rch = r;
}
}
}
測試驅動函數
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
String input = cin.nextLine();
double t = new ExpTree().solve(input);
T.d(t);
double s = new ExpStack().solve(input);
T.d(s);
}
}