/*
使用两个栈,一个数字栈,一个符号栈
从左往右遍历表达式字符串
1.遇到数字,直接压入数字栈
2.遇到符号
(1)遇到左括号,直接入符号栈
(2)遇到右括号,”符号栈弹栈取栈顶符号b,数字栈弹栈取栈顶数字a1,数字栈弹栈取栈顶数字a2,计算a2 b a1 ,将结果压入数字栈”,重复引号步骤至取栈顶为左括号,将左括号弹出
3.遇到运算符,
1)若该运算符的优先级大于栈顶元素的优先级,直接入符号栈。
2)若小于,”符号栈弹栈取栈顶符号b,数字栈弹栈取栈顶数字a1,数字栈弹栈取栈顶数字a2,计算a2 b a1 ,将结果压入数字栈”,重复引号步骤至该运算符的优先级大于符号栈顶元素的优先级,然后将该符号入符号栈
遍历结束后,”符号栈弹栈取栈顶符号b,数字栈弹栈取栈顶数字a1,数字栈弹栈取栈顶数字a2,计算a2 b a1 ,将结果压入数字栈”,重复引号步骤至符号栈无符号(或数字栈只有一个元素),则数字栈的元素为运算结果
*/
七、代码二
环境:
Eclipse Java EE IDE(Version: Oxygen.1a Release (4.7.1a))
jdk1.8.0_131
先写一个最基本的两位数四则运算方法,比较简单,没有写注释:
private static double doubleCal(double a1, double a2, char operator) throws Exception {
switch (operator) {
case '+':
return a1 + a2;
case '-':
return a1 - a2;
case '*':
return a1 * a2;
case '/':
return a1 / a2;
default:
break;
}
throw new Exception("illegal operator!");
}
写一个获得优先级的方法:
private static int getPriority(String s) throws Exception {
if(s==null) return 0;
switch(s) {
case "(":return 1;
case "+":;
case "-":return 2;
case "*":;
case "/":return 3;
default:break;
}
throw new Exception("illegal operator!");
}
解析表达式:
public static String getResult(String expr) throws Exception {
System.out.println("计算"+expr);
/*数字栈*/
Stack<Double> number = new Stack<Double>();
/*符号栈*/
Stack<String> operator = new Stack<String>();
operator.push(null);// 在栈顶压人一个null,配合它的优先级,目的是减少下面程序的判断
/* 将expr打散为运算数和运算符 */
Pattern p = Pattern.compile("(?<!\\d)-?\\d+(\\.\\d+)?|[+\\-*/()]");// 这个正则为匹配表达式中的数字或运算符
Matcher m = p.matcher(expr);
while(m.find()) {
String temp = m.group();
if(temp.matches("[+\\-*/()]")) {//遇到符号
if(temp.equals("(")) {//遇到左括号,直接入符号栈
operator.push(temp);
System.out.println("符号栈更新:"+operator);
}else if(temp.equals(")")){//遇到右括号,"符号栈弹栈取栈顶符号b,数字栈弹栈取栈顶数字a1,数字栈弹栈取栈顶数字a2,计算a2 b a1 ,将结果压入数字栈",重复引号步骤至取栈顶为左括号,将左括号弹出
String b = null;
while(!(b = operator.pop()).equals("(")) {
System.out.println("符号栈更新:"+operator);
double a1 = number.pop();
double a2 = number.pop();
System.out.println("数字栈更新:"+number);
System.out.println("计算"+a2+b+a1);
number.push(doubleCal(a2, a1, b.charAt(0)));
System.out.println("数字栈更新:"+number);
}
System.out.println("符号栈更新:"+operator);
}else {//遇到运算符,满足该运算符的优先级大于栈顶元素的优先级压栈;否则计算后压栈
while(getPriority(temp) <= getPriority(operator.peek())) {
double a1 = number.pop();
double a2 = number.pop();
String b = operator.pop();
System.out.println("符号栈更新:"+operator);
System.out.println("数字栈更新:"+number);
System.out.println("计算"+a2+b+a1);
number.push(doubleCal(a2, a1, b.charAt(0)));
System.out.println("数字栈更新:"+number);
}
operator.push(temp);
System.out.println("符号栈更新:"+operator);
}
}else {//遇到数字,直接压入数字栈
number.push(Double.valueOf(temp));
System.out.println("数字栈更新:"+number);
}
}
while(operator.peek()!=null) {//遍历结束后,符号栈数字栈依次弹栈计算,并将结果压入数字栈
double a1 = number.pop();
double a2 = number.pop();
String b = operator.pop();
System.out.println("符号栈更新:"+operator);
System.out.println("数字栈更新:"+number);
System.out.println("计算"+a2+b+a1);
number.push(doubleCal(a2, a1, b.charAt(0)));
System.out.println("数字栈更新:"+number);
}
return number.pop()+"";
}
主方法,以-3.5*(4.5-(4+(-1-1/2)))测试
public static void main(String[] args) throws Exception {
String str = "-3.5*(4.5-(4+(-1-1/2)))";
System.out.println(getResult(str));
}
/*
使用两个栈,一个数字栈,一个符号栈
从左往右遍历表达式字符串
1.遇到数字,直接压入数字栈
2.遇到符号
(1)遇到左括号,直接入符号栈
(2)遇到右括号,”符号栈弹栈取栈顶符号b,数字栈弹栈取栈顶数字a1,数字栈弹栈取栈顶数字a2,计算a2 b a1 ,将结果压入数字栈”,重复引号步骤至取栈顶为左括号,将左括号弹出
(3).遇到运算符,
1)若该运算符的优先级大于栈顶元素的优先级,直接入符号栈。
2)若小于,”符号栈弹栈取栈顶符号b,数字栈弹栈取栈顶数字a1,数字栈弹栈取栈顶数字a2,计算a2 b a1 ,将结果压入数字栈”,重复引号步骤至该运算符的优先级大于符号栈顶元素的优先级,然后将该符号入符号栈
3.遍历结束后,”符号栈弹栈取栈顶符号b,数字栈弹栈取栈顶数字a1,数字栈弹栈取栈顶数字a2,计算a2 b a1 ,将结果压入数字栈”,重复引号步骤至符号栈无符号(或数字栈只有一个元素),则数字栈的元素为运算结果
*/
//难点:区分负号和减号,数字-数字,为减号;右括号-数字,为减号,即-数字前面不是数字或者右括号为负数。
解决:正则表达式的正向预查和反向预查
import java.util.Scanner;
import java.util.Stack;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
String expr = sc.nextLine();
System.out.println(getResult(expr));
}
}
public static String getResult(String expr) {
//数字栈
Stack<Integer> number = new Stack<>();
//符号栈
Stack<String> operator = new Stack<>();
operator.push(null);//压入null,用于比较优先级
/* 将expr打散为运算数和运算符 */
//难点:区分负号和减号,数字-数字,为减号;右括号-数字,为减号,即-数字前面不是数字或者右括号为负数
Pattern p = Pattern.compile("(?<!\\d+|\\))-?\\d+|[+\\-*/()]");
Matcher m = p.matcher(expr);
while(m.find()) {
String temp = m.group();
if(temp.matches("[+\\-*/()]")) {
//遇到左括号,入栈
if(temp.equals("(")) {
operator.push(temp);
//遇到右括号,取符号栈栈顶符号b,数字栈栈顶a1,数字栈栈顶a2,计算a2 b a1 ,结果存入数字栈,直到遇到左括号,
}else if(temp.equals(")")) {
String b = null;
while(!(b=operator.pop()).equals("(")) {
int a1 = number.pop();
int a2 = number.pop();
number.push(cal(a2,a1,b.charAt(0)));
}
//遇到运算符,满足该运算符的优先级大于栈顶元素的优先级压栈;否则计算后压栈
}else {
while(getPriority(temp)<= getPriority(operator.peek())) {
String b =operator.pop();
int a1 = number.pop();
int a2 = number.pop();
number.push(cal(a2,a1,b.charAt(0)));
}
operator.push(temp);
}
}else {//遇到数字,入栈
number.push(Integer.valueOf(temp));
}
}
//遍历结束后,符号栈数字栈依次弹栈计算,并将结果压入数字栈
while(operator.peek()!=null ) {
int a1 = number.pop();
int a2 = number.pop();
String b = operator.pop();
number.push(cal(a2, a1, b.charAt(0)));
}
return number.pop()+"";
}
//两项的四则运算
public static Integer cal(int a1, int a2, char b) {
switch(b) {
case '+':
return a1+a2;
case '-':
return a1-a2;
case '*':
return a1*a2;
case '/':
return a1/a2;
default:
break;
}
return 0;
}
//获取优先级
public static int getPriority(String s) {
if(s==null) return 0;
switch(s) {
case "(":
return 1;
case "+":
case "-":
return 2;
case "*":
case "/":
return 3;
default:
break;
}
return 0;
}
}