需求
已知一个只含有()、+ 、-、*、\这五种操作符,数字只是整数,并且空格隔开的表达式字符串,求表达式的值。
例如:
( 3 + 4 ) * 5 - 6
结果为29
解题思路
给定表达式为中缀表达式,将其转为后缀表达式(逆波兰表达式)然后使用逆波兰表达式求解值。
逆波兰表达式求解值得思路:
- 遍历表达式,遇到数值则入栈,遇到计算符则从栈中弹出两个元素然后将计算结果压入栈中。做减和除法运算的时候一定要注意顺序,都是拿次栈顶去减/除栈顶元素。
- 遍历完毕后栈中的元素就是表达式的结果值。
Java实现
1. 中缀表达式转逆波兰表达式
思路:
首先需要一个栈operator用来存放计算符号和左括号(;
还需要一个集合来存放逆波兰表达式(这个集合可以是list、stack等)。
遍历表达式,得到值为item:
- 如果item为数值,则直接将item入中间结果集合中;
- 否则如果为"(",则直接入栈
- 否则如果为")",栈顶元素不是"(“时,将栈中元素不断弹出然后加入到中间结果集合中。结束之后将栈顶的”("弹出。这一步的目的是为了去括号
- 否则,当栈不为空并且栈顶元素的优先级 >= item的优先级时,不断的将栈中的元素弹出并添加到中间结果集合中。循环结束,将item入栈。
遍历结束后,集合中就是逆波兰表达式。
2. 计算逆波兰表达式的值
前提:
- 整数相除只取整数部分;
- 结果一定为整数(这里可以根据需求改成浮点型数据,并且加入除0判断并抛出异常)。
逆波兰表达式求值的思路前面已经讲过,这里不再敖述了。
3. 代码实现
public class CalculateExpression {
public static void main(String[] args) {
String expression = "( 3 + 4 ) * 5 - 6 - 3 * 3 - 2";
System.out.println(calculateExpression(expression));//结果18
}
public static int calculateExpression(String expression){
Stack<String> operator = new Stack<>();//操作符栈
List<String> list = new ArrayList<>();//结果集
String[] s = expression.split(" ");//表达式集合
//遍历表达式
for (int i = 0; i < s.length; i++) {
if (s[i].matches("\\d+"))//正则表达式匹配数字,对应思路1
list.add(s[i]);
else if (s[i].equals("(")){//对应思路2
operator.push(s[i]);
}else if (s[i].equals(")")){//对应思路3
while (!operator.peek().equals("(")){
list.add(operator.pop());
}
operator.pop();
}else{//对应思路4
while (!operator.isEmpty() && priority(operator.peek()) >= priority(s[i])){
list.add(operator.pop());
}
operator.push(s[i]);
}
}
while (!operator.isEmpty())
list.add(operator.pop());
Stack<Integer> nums = new Stack<>();
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("+")){
nums.push(nums.pop() + nums.pop());
}else if (list.get(i).equals("-")){
nums.push(-(nums.pop() - nums.pop()));
}else if (list.get(i).equals("*")){
nums.push(nums.pop() * nums.pop());
}else if (list.get(i).equals("/")){
int num1 = nums.pop();
int num2 = nums.pop();
nums.push(num2 / num1);
} else
nums.push(Integer.parseInt(list.get(i)));
}
return nums.pop();//弹出栈中元素返回结果
}
//计算符优先级
public static int priority(String oper){
if (oper.equals("+") || oper.equals("-"))
return 0;
else if (oper.equals("*") || oper.equals("/"))
return 1;
else return -1;
}
}
总结
表达式如果不带括号,只需要两个栈就能够完成结果的计算,不需要将表达式转逆波兰表达式这步操作。
但是逆波兰表达式的计算思路简单易懂,而且能做到一劳永逸所以我们可以使用这种思路来计算表达式的值。
表达式值的计算涉及到大量关于栈这种基础数据结构的操作,对栈的使用是一个非常好的练习。