前奏理解
中缀表达式
实现:使用栈,一个是数值栈、一个是存储符号栈的栈集
思路:
判断是否是运算符
1:比较当前字符与栈顶的符号作比较 如果<=栈顶运算符就将栈顶符号作比较运算 直到栈顶小于当前字符等级
2:将当前字符存储到符号栈中
判断是否是括号
1:左括号就new 一个Stack对象给符号栈集即可
2:右括号将当前符号栈中的依次取出与数值栈作比较即可
判断是否是数值
1:存入到数值栈中
依次遍历符号栈集中的符号与数值栈中作比较 直到数值栈中唯一元素就是结果
注意:中缀表达式可能会做运算的位置有:当前字符是运算符、当前字符是)、最后遍历符号栈集做运算
后缀表达式
实现:使用栈和队列,一个是存储符号的栈(转换的时候用到)、一个是存储数值及符号内容的后缀表达式队列(转换的时候用到)、一个是存储数值的栈(对后缀表达式计算用到)
思路:
判断是否是运算符
1:比较当前字符与栈顶的符号作比较 如果<=栈顶运算符就将栈顶符号就将栈符号存入到内容队列中 直到栈顶小于当前字符等级
2:将当前字符存储到符号栈中
判断是否是括号
1:将当前左括号存入到符号栈中
2:对符号栈依次取出 并存储到内容队列中 直到符号栈出现左括号为止
判断是否是数值
1:存入到内容队列中
依次将符号栈中的内容取出存入到内容队列中 直到结束内容队列从左往右看就是一个后缀表达式
然后将内容队列中从左往右取出如果是数值就放入到数值栈中如果是运算符 就与数值栈中做运算
注意:后缀表达式只有后转换完毕后才能一次的对数值内容做运算
提示:该片文章解决了 多位数、多括号、负数等问题,喜欢可以仔细的看
代码如下
public class InfixExpressionDemo {
public static void main(String[] args) {
System.out.println(InfixExpression.converInfixExpression("(70+20*((66-2)*(5+5*20-40)-60*5)*4-4)"));
}
}
//创建一个中缀表达式类
class InfixExpression{
//创建一个转中缀表达式的方法
public static Integer converInfixExpression(String expression){
//创建一个数值栈
Stack<Integer> numberStack=new Stack<>();
//创建一个符号栈存储每个括号下的符号信息
Stack<Stack<Character>> symbolStacks=new Stack<>();
//默认有一个符号栈
symbolStacks.push(new Stack<Character>());
//创建一个拼接数值的变量
String linkedNumber="";
//将表达式转换成字符数组
char[] expressionArr=expression.trim().toCharArray();
for(int index=0; index<expressionArr.length;index++){
//获取当前字符索引元素
char currentChar=expressionArr[index];
if(currentChar==' ')continue;
//判断是否是否是运算符
if(isSymbol(currentChar+"")){
//判断是否是减号
if(currentChar=='-'&&index==0||!isNumeric(expressionArr[index-1]+"")&&expressionArr[index-1]!=')'&&expressionArr[index-1]!=' '){
//再次确认当前减号是一个符号
linkedNumber+=currentChar;
}else{
//获取当前括号下的符号栈对象
Stack<Character> currentStack=symbolStacks.peek();
while(!currentStack.isEmpty()){
if(symbolLevel(currentChar)<=symbolLevel(currentStack.peek())){
//获取currentStack栈顶的运算符 与数组栈中的栈顶元素和次顶元素作比较
char currentSymbol=currentStack.pop();
int num1=numberStack.pop();
int num2=numberStack.pop();
//运算后的结果再次存入到数值栈中
numberStack.push(calculate(num1,num2,currentSymbol));
}else{
break;
}
}
//将当前符号存入到当前符号栈中
currentStack.push(currentChar);
}
}
//判断是否是括号
else if (currentChar=='(' ||currentChar==')'){
if(currentChar=='('){
//创建一个符号栈对象
Stack<Character> newSymbolStack=new Stack<>();
symbolStacks.push(newSymbolStack);
}else{
//获取当前对应的下符号栈对象
Stack<Character> currentSymbolStack=symbolStacks.pop();
//依次弹栈出符号与数值栈做比较
while(!currentSymbolStack.isEmpty()){
int num1=numberStack.pop();
int num2=numberStack.pop();
numberStack.push(calculate(num1,num2,currentSymbolStack.pop()));
}
}
}else{
//拼接当前数值字符
linkedNumber+=currentChar;
//判断是否表达式数组时候遍历到最后一个
if(index==expressionArr.length-1){
//将当linkedNumber装换成数值存入到数值栈中
numberStack.push(Integer.parseInt(linkedNumber));
}else{
//判断当前索引的下一个索引元素是否是数值如果是数值代表多位数
if(!isNumeric(expressionArr[index+1]+"")){
numberStack.push(Integer.parseInt(linkedNumber));
//将linkedNumber设置为“”
linkedNumber="";
}
}
}
}
//依次取出符号栈集中的符号与数值栈做比较
while(!symbolStacks.isEmpty()){
Stack<Character> currentStack=symbolStacks.pop();
while(!currentStack.isEmpty()){
int num1=numberStack.pop();
int num2=numberStack.pop();
numberStack.push(calculate(num1,num2,currentStack.pop()));
}
}
return numberStack.pop();
}
public static boolean isSymbol(String ch){
return ch.equals("+") || ch.equals("-") || ch.equals("*") || ch.equals("/");
}
/**
* 判断是否为数字(正负数都行)
* @param str 需要验证的字符串
* @return
*/
public static boolean isNumeric(String str){
Pattern pattern = Pattern.compile("^-?\\d+(\\.\\d+)?$");
Matcher isNum = pattern.matcher(str);
if( !isNum.matches() ){
return false;
}
return true;
}
/******************************
* 作者: 刘梓江
* 时间: 2019/12/12 19:58
* 描述: 判断符号等级 等级越大 数值就越大
******************************/
public static int symbolLevel(int ch){
if(ch=='+'|| ch=='-'){
return 1;
}else if(ch=='*'|| ch=='/'){
return 2;
}else{
return -1;
}
}
/******************************
* 作者: 刘梓江
* 时间: 2019/12/12 9:25
* 描述: 计算2个数值
******************************/
public static int calculate(int num1,int num2,Character ch){
switch (ch){
case '+':
return num2+num1; //由于是栈特点所以最前面的数取的时候最末尾
case '-':
return num2-num1; //由于是栈特点所以最前面的数取的时候最末尾
case '*':
return num2*num1; //由于是栈特点所以最前面的数取的时候最末尾
case '/':
return num2/num1; //由于是栈特点所以最前面的数取的时候最末尾
default:
return -1;
}
}
}