我。。。好久没有来写博客了,不是因为我没有刷题了,而是我卡在了算法训练的前几道题上,什么AC自动树,压缩状态dp,都要把我弄疯了,我以为后面的题的难度是递增的,搞得我都怀疑人生了,在被大佬嘲笑之后,我才发现后面的题要简单多了,于是先做几道题重拾信心,今天就是表达式计算
题目描述:
输入一个只包含加减乖除和括号的合法表达式,求表达式的值。其中除表示整除。(也就是给你一个中缀表达式求出它的值)
**题目分析:**这道题很短,给人一种几分钟就可以搞定的感觉,但是小小身体蕴含大大能量。这道题我的思路是, 首先给定的是一个中缀表达式,那么我们的任务就是求出这个中缀表达式的值,因为之前做过这种题,一下就想到了栈,定义两个栈,一个用来存放操作数(Integer),一个用来存放操作符(Character),
规则:
- 首先我们在操作符栈先放一个字符‘#’,(为什么要放这个操作符呢?是为了防止出现空栈的情况,那这里我就要反问你了,既然你为了防止出现空栈,你为什么不将操作数栈中也先放一个数,这里我要说明的是,在我们读取输入的表达式时,我们的计算都是根据操作符来进行的,后面你就会知道了)
- 然后我们读取输入的表达式,我们用一个字符串来存放,然后用charAt()来分别获取每一个字符,但是这里需要注意的是如果我们的操作数是两位或者两位以上,那这里就需要有特殊处理,如果当前读入的是数字我们就继续往下读直到读到操作符为止,记录当前的下标和之前的下标,对字符串进行截取,然后转化为int类型,
- 然后对我们读入的数据进行判断,如果是操作数,我们就直接进栈,如果是操作符,取出操作符的栈顶元素,判断栈顶元素和读入操作符的优先级(’ # '的优先级为0, ’ + ’ ’ - '的优先级为1, '* ’ ’ / '的优先级为2 ’ ( '的优先级为3 ),如果读入的操作符的优先级更高,我们就让他直接进栈,否则,我们就让操作数栈出栈两个数,操作符栈出栈栈顶元素,然后进行运算,在将运算结果放到操作数栈,然后继续判断当前操作符和操作符栈的栈顶元素的优先级(这里我会单独写成一个方法 opration() )。
- 如果中缀表达式中没有括号,那么我们的任务就完成了,但是表达式中式会出现括号的,所以我们还要继续考虑,当我们读入到‘( ’我们直接像之前的操作一样让他入栈,然后继续读入,如果是数字就进操作数栈,因为我将‘( ‘的优先级是设置的最高的,如果按照之前的方法就是错误的,所以当读入操作符的时候我们增加一个选项,是否栈顶元素是’( ’ 如果是的话 ,就将该操作符入栈,后面的操作和之前一样了(执行3),直到读到了’ ’ )’ 我们就进行 opration(),直到遇到了’ ( '将其出栈,
- 执行3。当读入完毕了之后,我们判断操作符栈是否读到了结尾,也就是栈顶元素是否是’#‘,然后对栈进行操作。直到操作符栈的栈顶为’#‘。
- 输出操作数的栈顶元素
我在写代码时没有考虑到的问题:
- 我在处理当读入括号的时候,一直不知道放到哪里处理,结果经过了反复而又痛苦的尝试后,我才知道将其放到run函数中
- 没有考虑到操作数不仅仅只有一位的情况
- 当操作符的优先级小于栈顶元素的优先级,没有考虑到重复操作,以及读到’(‘的情况。
献上代码:
package algothm_training;
import java.util.Scanner;
import java.util.Stack;
//表达式计算 我竟然忘了 怎样将一个类放到数组中来
public class Main_expresioncalulate03 {
static Stack<Integer> number=new Stack<Integer>(); //操作数栈
static Stack<Character> oprate=new Stack<Character>(); //操作符栈
static String expresion;
static int i=0;
public static void main(String[] args) { //2*(3+5)+7/1-4
Scanner scanner =new Scanner(System.in);
expresion=scanner.nextLine();
oprate.push('#'); //先将操作符栈中放入# 好进行之后的优先级比较
while(i<expresion.length()) {
char temp=expresion.charAt(i);
run(temp);
i++;
}
while(oprate.peek()!='#') { //如果操作符栈的栈顶元素不是#
opration();
}
System.out.println(number.peek()); //输出操作数栈的栈顶元素
}
static void run(char temp) {
if(temp>=48&&temp<=57) {
int k=i+1;
while(k<expresion.length()&&expresion.charAt(k)>=48&&expresion.charAt(k)<=57) { //如果l还是数字
k++;
}
int num=Integer.parseInt(expresion.substring(i,k));
i=k-1;
number.push(num);
}else {
char top=oprate.peek();
int top_first=first(top); //栈顶元素的优先级
int temp_first=first(temp); //当前要插入的元素的优先级
if(temp_first>top_first||top=='(') {
oprate.push(temp);
}else if(temp==')') {
while(temp!='(') {
opration();
temp=oprate.peek();
}
oprate.pop();
}else{
while(temp_first<=top_first&&top!='(') {//哦哦这里是有问题的!!!!!!!!!!
opration();
top=oprate.peek(); //再次取出栈顶元素 看栈顶元素和当前元素的优先级
top_first=first(top);
}
oprate.push(temp);
}
}
}
//出栈计算
static void opration(){
char oprate0=oprate.pop();
int num1=number.pop();
int num2=number.pop();
int num3;
switch(oprate0) {
case '+':
num3=num2+num1;
break;
case '-':
num3=num2-num1;
break;
case '*':
num3=num2*num1;
break;
case '/':
num3=num2/num1;
break;
default:
num3=0;
break;
}
number.push(num3);
}
//返回符号优先级
static int first(char operate) {//478*7*2-82348/2+(83*23-6466)
int fir=0;
switch(operate) {
case '#':
fir=0;
break;
case '+':
case '-':
fir=1;
break;
case '*':
case '/':
fir=2;
break;
case '(':
fir=3;
default:
break;
}
return fir;
}
}