最近在学习算法时候,看到一个双栈算术表达式求值算法,挺启发我的;平常大家在写代码的时候要写表达式的时候,基本都是直接 (1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) );这样程序会帮你的自动计算出结果,大家有没用想过这程序是如何一步一步计算并得出你需要的值,今天讲一个简单例子,可以帮助大家很好的了解双栈算术表达式求值算法是什么。
首先,为了简单化的解释该算法,我们定义一个未省略括号的表达式:(1 + ( ( 2 + 3 ) * ( 4 * 5 ) ) )
算法思路
双栈算术表达式求值算法是由E.W.Dijkstra在上个世纪60年代发明的一个很简单的算法,用两个栈【一个用来保存运算符、一个用来保存操作数】来完成对一个表达式的运算。其实整个算法思路很简单:
- 无视左括号
- 将操作数压入操作数栈
- 将运算符压入运算符栈
- 在遇到右括号的时候,从运算符栈中弹出一个运算符,再从操作数栈中弹出所需的操作数,并且将运算结果压入操作数栈中
算法过程图示
(这个是《算法 第四版》里面的算法轨迹图,感觉很生动形象)
代码实现(这里是用java)
public class MyDijkstraAlgorithm {
public static void main(String[] args) {
dijkstraAlgorithm("(1+((2+3)*(4*5)))");
}
public static void dijkstraAlgorithm (String str) {
//创建操作符栈
Stack<Character> ops = new Stack<>();
//创建操作数栈
Stack<Double> nums = new Stack<>();
for (int i = 0 ; i < str.length(); i++) {
char s = str.charAt(i);
switch (s) {
//无视左括号
case '(':
break;
// + 、 - 、 * 、/、sqrt 这些操作符都压入栈
case '+':
ops.push(s);
break;
case '-':
ops.push(s);
break;
case '*':
ops.push(s);
break;
case '/':
ops.push(s);
break;
case ')':
//遇到右括号时,根据从操作符栈中取出操作符,并从操作数栈中取出相应都操作数进行计算,并将计算结果压入操作数栈
char op = ops.pop();
Double num = nums.pop();
switch (op) {
case '+':
num = num + nums.pop();
break;
case '-':
num = num - nums.pop();
break;
case '*':
num = num * nums.pop();
break;
case '/':
num = num / nums.pop();
break;
}
nums.push(num);
break;
default:
nums.push(Double.parseDouble(Character.toString(s)));
break;
}
}
System.out.println(nums.pop());
}
}
最后说两句
这个算法虽然看起来实用性不大,但是它充分利用了栈的特性,执行效率也比较高,我可以学习它的思路,把一些比较复杂的问题给简化了;