蓝桥杯算法训练 java算法 表达式求值

时间:2022-06-20 03:15:19
问题描述
  输入一个只包含加减乖除和括号的合法表达式,求表达式的值。其中除表示整除。
输入格式
  输入一行,包含一个表达式。
输出格式
  输出这个表达式的值。
样例输入
1-2+3*(4-5)
样例输出
-4
问题分析:

1将运算式子转化为后缀表达式
2扫描后缀表达式,将数字依次进栈,当到运算符号时,出栈两个数字,进行运算,再将结果放进数字栈中
3最后数字栈最上面得到的即为结果,边扫描边进行运算
4关键点在后缀表达式的转化,下面是后缀表达式的要求
  4.1读到运算数字时,直接输出;
  4.2栈为空时,读到操作符,直接进栈;
  4.3左括号的优先级被视为比所有运算符低,读到左括号,直接进栈;
  4.4读到右括号,把栈中左括号上面的元素输出,并把左括号弹出;
  4.5读到其他运算符,输出所有优先级大于或等于该运算符的栈顶元素;
  4.6最后把栈中剩余的元素一一输出。

  4.7后缀表达式中没有()左右两个括号

例如该例子中5+12*(3+5)/7的后缀表达式为5 12 3 5 + * 7 / +
5有了后缀表达式进行计算
  5.1、 5 12 3 5 当扫描到+时3 5 出栈进行运算得到8 在进栈 栈中现在有5 12 8
  5.2、扫描到*时,12和8出栈,进行运算,得到96,进栈,栈中有5 96
  5.3、扫描到7,进栈,栈中有5 96 7
  5.4、扫描到/,96和7出栈,进行运算得到13.714....,进栈,栈中有5 13.714....
  5.5、扫描到+,5 13.714出栈,进行运算,得到18.714....这就是结果

下面是java代码,已经在蓝桥杯系统验证成功,此算法提示是整除,所以不用考虑小数问题

 import java.util.Collections;
import java.util.Scanner;
import java.util.Stack;
public class Main {
private Stack<String> houzhuiStack = new Stack<String>();//后缀式栈
private Stack<Character> yunsuanfuStack = new Stack<Character>();//运算符栈
private int [] operatPriority = new int[] {0,3,2,1,-1,1,0,2};//运用运算符ASCII码-40做索引的运算符优先级
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
Main cal = new Main();
String result = cal.calculate(s);
System.out.println(result);
}
/*
*2.得到后缀表达式进行运算
*/
public String calculate(String expression) {
Stack<String> resultStack = new Stack<String>();
prepare(expression);
Collections.reverse(houzhuiStack);//将后缀式栈反转
String firstValue ,secondValue,currentValue;//参与计算的第一个值,第二个值和算术运算符
while(!houzhuiStack.isEmpty()) {
currentValue = houzhuiStack.pop();
if(!isOperator(currentValue.charAt(0))) {//如果不是运算符则存入操作数栈中
resultStack.push(currentValue);
} else {//如果是运算符则从操作数栈中取两个值和该数值一起参与运算
secondValue = resultStack.pop();
firstValue = resultStack.pop();
String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
resultStack.push(tempResult);
}
}
return resultStack.pop();
}
/**
* 1数据准备阶段将表达式转换成为后缀式栈
*/
private void prepare(String expression) {
yunsuanfuStack.push(',');//运算符放入栈底元素逗号,此符号优先级最低
char[] arr = expression.toCharArray();
int currentIndex = 0;//当前字符的位置
int count = 0;//上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
char currentOp ,peekOp;//当前操作符和栈顶操作符
for(int i=0;i<arr.length;i++) {
currentOp = arr[i];
if(isOperator(currentOp)) {//如果当前字符是运算符
if(count > 0) {
houzhuiStack.push(new String(arr,currentIndex,count));//取两个运算符之间的数字
}
peekOp = yunsuanfuStack.peek();
if(currentOp == ')') {//遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
while(yunsuanfuStack.peek() != '(') {
houzhuiStack.push(String.valueOf(yunsuanfuStack.pop()));
}
yunsuanfuStack.pop();
} else {
while(currentOp != '(' && peekOp != ',' && compare(currentOp,peekOp) ) {
houzhuiStack.push(String.valueOf(yunsuanfuStack.pop()));
peekOp = yunsuanfuStack.peek();
}
yunsuanfuStack.push(currentOp);
}
count = 0;
currentIndex = i+1;
} else {
count++;
}
}
if(count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {//最后一个字符不是括号或者其他运算符的则加入后缀式栈中
houzhuiStack.push(new String(arr,currentIndex,count));
} while(yunsuanfuStack.peek() != ',') {
houzhuiStack.push(String.valueOf( yunsuanfuStack.pop()));//将操作符栈中的剩余的元素添加到后缀式栈中
}
}
/**
* 判断是否为算术符号
*/
private boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' ||c == ')';
}
/**
* 利用ASCII码-40做下标去算术符号优先级
*/
public boolean compare(char cur,char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
boolean result = false;
if(operatPriority[(peek)-40] >= operatPriority[(cur) - 40]) {
result = true;
}
return result;
}
/**
* 按照给定的算术运算符做计算
*/
private String calculate(String firstValue,String secondValue,char currentOp) {
String result = "";
switch(currentOp) {
case '+':
result = String.valueOf(ArithHelper.add(firstValue, secondValue));
break;
case '-':
result = String.valueOf(ArithHelper.sub(firstValue, secondValue));
break;
case '*':
result = String.valueOf(ArithHelper.mul(firstValue, secondValue));
break;
case '/':
result = String.valueOf(ArithHelper.div(firstValue, secondValue));
break;
}
return result;
}
static class ArithHelper {
// 这个类不能实例化
private ArithHelper() {
}
/**
* 提供精确的加法运算。
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static String add(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return String.valueOf(b1.add(b2).intValue());
}
/**
* 提供精确的减法运算。
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/
public static String sub(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return String.valueOf(b1.subtract(b2).intValue());
}
/**
* 提供精确的乘法运算。
* @param v1
* 被乘数
* @param v2
* 乘数
* @return 两个参数的积
*/
public static String mul(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return String.valueOf(b1.multiply(b2).intValue());
}
/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
* @param v1
* 被除数
* @param v2
* 除数
* @return 两个参数的商
*/
public static String div(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return String.valueOf(b1.divideToIntegralValue(b2).intValue());
}
}
}

下面的代码是考虑了不识字整除的情况。仅供参考

 import java.util.Collections;
import java.util.Scanner;
import java.util.Stack;
public class 表达式求值 {
private Stack<String> houzhuiStack = new Stack<String>();//后缀式栈
private Stack<Character> yunsuanfuStack = new Stack<Character>();//运算符栈
private int [] operatPriority = new int[] {0,3,2,1,-1,1,0,2};//运用运算符ASCII码-40做索引的运算符优先级
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
表达式求值 cal = new 表达式求值();
double result = cal.calculate(s);
System.out.println(result);
}
/*
*2.得到后缀表达式进行运算
*/
public double calculate(String expression) {
Stack<String> resultStack = new Stack<String>();
prepare(expression);
Collections.reverse(houzhuiStack);//将后缀式栈反转
String firstValue ,secondValue,currentValue;//参与计算的第一个值,第二个值和算术运算符
while(!houzhuiStack.isEmpty()) {
currentValue = houzhuiStack.pop();
if(!isOperator(currentValue.charAt(0))) {//如果不是运算符则存入操作数栈中
resultStack.push(currentValue);
} else {//如果是运算符则从操作数栈中取两个值和该数值一起参与运算
secondValue = resultStack.pop();
firstValue = resultStack.pop();
String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
resultStack.push(tempResult);
}
}
return Double.valueOf(resultStack.pop());
} /**
* 1数据准备阶段将表达式转换成为后缀式栈
*/
private void prepare(String expression) {
yunsuanfuStack.push(',');//运算符放入栈底元素逗号,此符号优先级最低
char[] arr = expression.toCharArray();
int currentIndex = 0;//当前字符的位置
int count = 0;//上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
char currentOp ,peekOp;//当前操作符和栈顶操作符
for(int i=0;i<arr.length;i++) {
currentOp = arr[i];
if(isOperator(currentOp)) {//如果当前字符是运算符
if(count > 0) {
houzhuiStack.push(new String(arr,currentIndex,count));//取两个运算符之间的数字
}
peekOp = yunsuanfuStack.peek();
if(currentOp == ')') {//遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
while(yunsuanfuStack.peek() != '(') {
houzhuiStack.push(String.valueOf(yunsuanfuStack.pop()));
}
yunsuanfuStack.pop();
} else {
while(currentOp != '(' && peekOp != ',' && compare(currentOp,peekOp) ) {
houzhuiStack.push(String.valueOf(yunsuanfuStack.pop()));
peekOp = yunsuanfuStack.peek();
}
yunsuanfuStack.push(currentOp);
}
count = 0;
currentIndex = i+1;
} else {
count++;
}
}
if(count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {//最后一个字符不是括号或者其他运算符的则加入后缀式栈中
houzhuiStack.push(new String(arr,currentIndex,count));
} while(yunsuanfuStack.peek() != ',') {
houzhuiStack.push(String.valueOf( yunsuanfuStack.pop()));//将操作符栈中的剩余的元素添加到后缀式栈中
}
} /**
* 判断是否为算术符号
*/
private boolean isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' ||c == ')';
} /**
* 利用ASCII码-40做下标去算术符号优先级
*/
public boolean compare(char cur,char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
boolean result = false;
if(operatPriority[(peek)-40] >= operatPriority[(cur) - 40]) {
result = true;
}
return result;
} /**
* 按照给定的算术运算符做计算
*/
private String calculate(String firstValue,String secondValue,char currentOp) {
String result = "";
switch(currentOp) {
case '+':
result = String.valueOf(ArithHelper.add(firstValue, secondValue));
break;
case '-':
result = String.valueOf(ArithHelper.sub(firstValue, secondValue));
break;
case '*':
result = String.valueOf(ArithHelper.mul(firstValue, secondValue));
break;
case '/':
result = String.valueOf(ArithHelper.div(firstValue, secondValue));
break;
}
return result;
} static class ArithHelper {
// 默认除法运算精度
private static final int DEF_DIV_SCALE = 16;
// 这个类不能实例化
private ArithHelper() {
} /**
* 提供精确的加法运算。
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/ public static double add(double v1, double v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
} public static double add(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.add(b2).doubleValue();
} /**
* 提供精确的减法运算。
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/ public static double sub(double v1, double v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
} public static double sub(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.subtract(b2).doubleValue();
} /**
* 提供精确的乘法运算。
* @param v1
* 被乘数
* @param v2
* 乘数
* @return 两个参数的积
*/ public static double mul(double v1, double v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
} public static double mul(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.multiply(b2).doubleValue();
} /**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
* @param v1
* 被除数
* @param v2
* 除数
* @return 两个参数的商
*/ public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
} public static double div(String v1, String v2) {
java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
} /**
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/ public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
} /**
* 提供精确的小数位四舍五入处理。
* @param v 需要四舍五入的数字
* @param scale 小数点后保留几位
* @return 四舍五入后的结果
*/ public static double round(double v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
java.math.BigDecimal one = new java.math.BigDecimal("1");
return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
} public static double round(String v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
java.math.BigDecimal b = new java.math.BigDecimal(v);
java.math.BigDecimal one = new java.math.BigDecimal("1");
return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
}