1.表达式求值比较复杂,涉及到运算符的优先级,还有括号的影响,所以需要有一个合适的求值方法来完成,逆波兰式是个最佳选择,逆波兰式就是利用堆的特性来设计的。
2.利用堆实现数学表达式求值,代码中主要的方法是initRPN方法和caculate方法,initRPN方法的功能是将输入的表达式(中缀表达式)按照规则转换成后缀表达式(逆波兰表达式),caculate是将逆波兰表达式按照规则计算结果。
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* 逆波兰表达式的实现
* 利用堆
*
* 输入
* String regex = "9+(3-1)*3+10/2";
* 预期输出
* String rpn_str = "931- 3*+102/+";
*
*/
public class RPN {
private static Map<String, Integer> optPriority = new HashMap<>();
private static final String NONE = "none";
/*这个是运算符的优先级,注意这里不要对括号排优先级,因为括号出现的场景不一样,运算符有可能进栈或者出栈,所以括号应当单独处理*/
static {
optPriority.put("+", 1);
optPriority.put("-", 1);
optPriority.put("*", 2);
optPriority.put("/", 2);
optPriority.put("=", 0);
optPriority.put("none", -Integer.MAX_VALUE);//辅助值,用以处理为null的情况
}
public static void main(String[] args) {
Stack<Object> rpn1 = initRPN();
Stack<Object> rpn2 = new Stack<>();
while (rpn1.top != null) {
rpn2.push(rpn1.pop());
}
caculate(rpn2);
}
/**
* 规则:从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符
* 号,就将处于栈顶两个数字出拢,进行运算,运算结果进栈, 一直到最终获得结果。
* @param rpn
*/
public static void caculate(Stack<Object> rpn) {
Stack<Integer> statck = new Stack<>();
if (rpn != null) {
while (rpn.getTop() != null) {
Object obj = rpn.pop();
if (isDigit((String) obj)) {
statck.push(Integer.valueOf((String)obj));
} else {
switch ((String) obj) {
case "+" : {
int num = statck.pop() + statck.pop();
statck.push(num);
break;
}
case "-" : {
int num = -statck.pop() + statck.pop();
statck.push(num);
break;
}
case "*" : {
int num = statck.pop() * statck.pop();
statck.push(num);
break;
}
case "/" : {
int a2 = statck.pop();
int a1 = statck.pop();
int num = a1 / a2;
statck.push(num);
break;
}
}
}
}
System.out.println("result: " + statck.pop());
}
}
/**
* 将中缀表达式转换成后缀表达式(逆波兰表达式)
* 规则 : 从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后
* 缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低
* 于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出 , 并将当前符号进栈,一直
* 到最终输出后缀表达式为止。
* @return
*/
public static Stack<Object> initRPN() {
Stack<Object> rpn = new Stack<>();
Scanner sc = new Scanner(System.in);
String e = sc.nextLine();
Stack<String> opt = new Stack<>();
while (true){
if (!isDigit(e)){
if ("(".equals(e)) {
opt.push(e);
}
if (")".equals(e)) {
String opt_top = opt.getTop() == null ? NONE : opt.pop();
while(!opt_top.equals("(")) {
rpn.push(opt_top);
opt_top = opt.pop();
}
}
if (!"(".equals(e) && !")".equals(e) && !"=".equals(e)) {
String opt_top = opt.getTop() == null ? NONE : opt.getTop();
while(true) {
if ("(".equals(opt_top)) {
opt.push(e);
break;
} else {
if (isBigger(e, opt_top)) {
opt.push(e);
break;
}
opt_top = opt.pop();
rpn.push(opt_top);
opt_top = opt.getTop() == null ? NONE : opt.getTop();
}
}
}
} else {
rpn.push(e);
}
if (e.equals( "=")) {
while (opt.getTop() != null) {
rpn.push(opt.pop());
}
break;
}
e = sc.nextLine();
}
return rpn;
}
public static boolean isBigger(String e, String topOpt) {
if (optPriority.get(e) > optPriority.get(topOpt))
return true;
else
return false;
}
//判断一个字符是否都为数字
public static boolean isDigit(String strNum){
return strNum.matches("[0-9]{1,}");
}
}
class Stack<T> {
Node<T> top;
public void push(T data) {
Node<T> newNode = new Node<>(data);
newNode.next = this.top;
this.top = newNode;
}
public T getTop() {
T topData = this.top == null ? null : this.top.data;
return topData;
}
public T pop() {
if (this.top != null) {
T data = this.top.data;
this.top = this.top.next;
return data;
} else {
System.out.println("堆空了!");
return null;
}
}
}
class Node<T> {
T data;
Node<T> next;
public Node(T data) {
this.data = data;
}
}