在上篇文章的最后留下了一个问题,求表达式4 + 2 * 3 - 10/5 的值。
对于计算机来说,它看到的就是用户输入的字符串,这个字符串是由运算符和数字组成的。为了使问题简单化,我们这里仅讨论最常见的加减乘除四则运算。那么如何解析出运算符和数字以及如何处理运算符的优先级问题呢?
我们可以通过上篇文章学习到的栈来解决,这里需要两个栈,一个栈用来保存数字,另一个栈保存运算符。下面我将一步一步展示表达式4 + 2 * 3 - 10 / 5 的计算过程:
为了解析出操作数和运算符我们需要从左向右遍历表达式。当遇到数字,就继续向后遍历直到遇到运算符。
为了正确解析出数字,我们这里定义了index变量,初始值为0,指向字符串的第1个元素。
继续向后遍历
这个时候i指向的是一个加运算符(+),我们可以通过index和 i 来截取出操作数4,Java中使用substring(index, i)
方法,同时将变量i 的值赋值给index,用于截取下一个操作数。
接下来,判断运算符栈是否为空,如果此时运算符栈为空就直接将当前运算符压入栈。否则通过peek方法获取运算符栈的栈顶元素,将当前运算符与运算符栈顶元素进行优先级比较。如果当前运算符比栈顶元素的优先级高,同样将当前运算符入栈。
如果比栈顶元素的优先级低或者相同,就从运算符栈顶取出(pop)运算符,并从操作数栈的栈顶取出2个操作数进行运算,再把运算结果压入操作数栈。我画了张图,希望对你的理解有所帮助。

上述过程计算完后,再将当前运算符压入栈,继续向后遍历。
为了栈能支持字符和数字,笔者对上篇文章中的ArrayStack类进行了扩展,同时增加了isEmpty()和peek()方法,完整代码如下所示:
public class ArrayStack<T> {
private Object[] items;
private final int stackSize;
private int top;
public ArrayStack(int stackSize) {
this.stackSize = stackSize;
items = new Object[stackSize];
}
public boolean push(Object item) {
if(top == stackSize) {
System.out.println("栈已满");
return false;
}
items[top++] = item;
return true;
}
public T pop() {
if(top == 0) {
System.out.println("栈为空");
return null;
}
return (T)items[--top];
}
/**
* 返回栈顶元素
* @return
*/
public T peek() {
if(top == 0) {
System.out.println("栈为空");
return null;
}
return (T)items[top-1];
}
/**
* 判断栈是否为空
* @return
*/
public boolean isEmpty() {
return top == 0;
}
public void list() {
for(int i = top-1; i >=0; i--) {
System.out.println(items[i]);
}
}
}
表达式求值
public class MathExpress {
public static void main(String[] args) {
String express = "4 + 2 + 3 * 10 / 5";
// 操作数栈
ArrayStack<Double> numStack = new ArrayStack(10);
// 运算符栈
ArrayStack<Character> opStack = new ArrayStack(10);
// 记录数字开始位置,用于从字符串中截取数字
int index = 0;
for(int i = 0, len = express.length(); i < len; i++) {
char c = express.charAt(i);
if(c == '+' || c == '-' || c == '*' || c == '/') {
// 从字符串中截取出数字
String value = express.substring(index, i).trim();
Double num = Double.parseDouble(value);
numStack.push(num);
index = i + 1;
// 如果运算符栈为空,直接入栈
if(opStack.isEmpty()) {
opStack.push(c);
// 继续下一次循环
continue;
}
// 取出栈顶运算符
Character op = opStack.peek();
if(compare(c, op) > 0) {
// 当前运算符优先级高于栈顶运算符优先级,则直接将当前运算符入栈
opStack.push(c);
} else {
// 当前运算符优先级低于或等于栈顶运算符,则从操作数栈取出2个操作数与栈顶运算符执行计算
Double v1 = numStack.pop();
Double v2 = numStack.pop();
double res = calc(v1, v2, opStack.pop());
// 将计算结果入操作数栈
numStack.push(res);
// 将当前运算符入栈
opStack.push(c);
}
}
// 最后一个数字
if(i == len - 1) {
Double num = Double.parseDouble(express.substring(index, len).trim());
numStack.push(num);
}
}
// 计算最终结果
while (!opStack.isEmpty()) {
Double v1 = numStack.pop();
Double v2 = numStack.pop();
double res = calc(v1, v2, opStack.pop());
numStack.push(res);
}
System.out.println(express +" = " + numStack.pop());
}
/**
* 计算
* @param v1
* @param v2
* @param c
* @return
*/
public static double calc(double v1, double v2, char c) {
switch (c) {
case '+':
return v2 + v1;
case '-':
return v2 - v1;
case '*':
return v2 * v1;
case '/':
return v2 / v1;
default:
throw new IllegalArgumentException("不支持的运算符" + c);
}
}
/**
* 比较运算符优先级
* @param c1
* @param c2
* @return
*/
public static int compare(char c1, char c2) {
return priority(c1) - priority(c2);
}
/**
* 获取运算符优先级
* @param c
* @return
*/
private static int priority(char c) {
if(c == '+' || c == '-') {
return 0;
}else if(c == '*' || c == '/') {
return 1;
}
throw new IllegalArgumentException("不支持的运算符" + c);
}
}
「更多精彩内容请关注公众号geekymv,喜欢请分享给更多的朋友哦」