目录
1.认识中缀表达式和后缀表达式
中缀表达式就是我们平常输入的计算表达式,比如:1.5*5+4^2*5.544+(4+5)*2,那么假设我们在计算机上输入前面这个后缀表达式,那么计算机是怎么识别、判断出各符号以及其优先级呢?原理其实就在后缀表达式中,将中缀表达式转化为后缀表达式后,会将()去掉,并且将数字和符号按照特定的顺序排列,以供后缀表达式的计算,那么我们接下来就看一下具体各部分是怎么实现的。
2.中缀表达式转后缀表达式
在代码实现之前,我们要理解中缀转后缀的原理,一个较简单的方法就是按照优先级将各部分用()括起来,比如上面的1.5*5+4^2*5.544+(4+5)*2,用户()括起来后就是 ( ( (1.5 * 5) +( (4 ^ 2 ) * 5.544 ) ) + ( ( 4 + 5 ) * 2 ) ),那么将转化为后缀表达式的方法就是按照优先级从大到小,将每一个括号内的符号提到 右括号 的右边,并且每个符号仅提取一次,那么我们将上面的表达式转化为后缀表达式后,就是 1.5 5 * 4 2 ^ 5.544 * + 45 + 2 * +
我们再仔细研究一下这个后缀表达式和中缀表达式的关系,会发现以下几点:
(1)数字的顺序是不变的
(2)符号的顺序实际上是按照各个括号内的优先级来排序的
那么我们在实现代码的时候就可以遍历中缀表达式的字符串,遇到数字直接将其拼接在后缀表达式字符串的后面,遇到符号就找到优先级最大的,依次排列(这个我们可以通过栈来实现,后面仔细讲),遇到括号就先算排列括号里面的表达式,接下来我们一部分一部分实现
注意:得出的后缀表达式每一部分后面会带一个空格,以便大于10的数字、浮点数的识别以及后续的计算
public static String inFixToSuffix(String inFix//传入中缀表达式) {
Stack<Character> stack = new Stack<>();//用于后续字符的排序
StringBuffer sufFix = new StringBuffer();//存放后缀表达式
}
2.1遇到数字
遇到数字我们直接将数字拼接在后缀表达式字符串后面即可,但是要注意大于10的数字,要将其完整数字提取出来整体拼接在后缀表达式后面
for (int i = 0; i < inFix.length(); i++) {
char ch = inFix.charAt(i);
if(ch == ' '){//遇到空格就跳过
continue;
}
if(ch >= '0' && ch <= '9') {
StringBuffer tmp = new StringBuffer();
while (i < inFix.length() && ((inFix.charAt(i) >= '0' && inFix.charAt(i) <= '9')|| inFix.charAt(i) == '.')) {
tmp.append(inFix.charAt(i));
i++;
}
i--;
sufFix.append(tmp.toString() + " ");
}
}
2.2遇到括号
仔细观察中缀转后缀的表达式,会发现实际上括号里面的符号最后会按照优先级一同排列在外边,那么我们遇到左括号就可以将左括号压在栈里,遇到右括号后就把压在栈里的符号依次拼接在后缀表达式后面,直到遇到左括号,说明括号里的符号已经排列出来了,这里不懂的话可以先看理解后面遇到符号的情况,再回来看
else if(ch == '('){
stack.push(ch);
}else if(ch == ')'){
while(!stack.empty() && stack.peek() != '(') {
sufFix.append(stack.pop() + " ");//将栈里的符号依次拼接
}
stack.pop();//弹出左括号
}
2.3遇到符号
首先我们要写一个方法用来判断优先级
public static int priorityJudge(char ch){
switch (ch) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
}
return -1;
}
前面说过,后缀表达式里的符号排序是按照中缀表达式中的符号的优先级进行排序的,那么我们在遇到符号的时候,可以先把他压在栈里,接下来继续遍历,如果下一个遇到的符号优先级比前一个还大,继续压在栈里,直到遇到下一个符号的优先级比前一个的小,说明前一个的优先级是最大的,那么就把他从栈中取出,拼接在后缀表达式后面
else {
while(!stack.empty() && priorityJudge(ch) <= priorityJudge(stack.peek())) {
sufFix.append(stack.pop() + " ");
}
stack.push(ch);
}
}
2.4遍历完后,把压在栈里的符号依次取出,拼接即可
while(!stack.empty()) {
if(stack.peek() == '(') {
return "inFix error";
}
sufFix.append(stack.pop() + " ");
}
2.5中缀转后缀的总体呈现
public static int priorityJudge(char ch){
switch (ch) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
}
return -1;
}
public static String inFixToSuffix(String inFix) {
Stack<Character> stack = new Stack<>();
StringBuffer sufFix = new StringBuffer();
for (int i = 0; i < inFix.length(); i++) {
char ch = inFix.charAt(i);
if(ch >= '0' && ch <= '9') {
StringBuffer tmp = new StringBuffer();
while (i < inFix.length() && ((inFix.charAt(i) >= '0' && inFix.charAt(i) <= '9')|| inFix.charAt(i) == '.')) {
tmp.append(inFix.charAt(i));
i++;
}
i--;
sufFix.append(tmp.toString() + " ");
}else if(ch == '('){
stack.push(ch);
}else if(ch == ')'){
while(!stack.empty() && stack.peek() != '(') {
sufFix.append(stack.pop() + " ");
}
stack.pop();
} else {
while(!stack.empty() && priorityJudge(ch) <= priorityJudge(stack.peek())) {
sufFix.append(stack.pop() + " ");
}
stack.push(ch);
}
}
while(!stack.empty()) {
if(stack.peek() == '(') {
return "inFix error";
}
sufFix.append(stack.pop() + " ");
}
return sufFix.toString();
}
}
3.后缀表达式的计算
我们还是先将原理
后缀表达式的计算方法是:
遍历后缀表达式,遇到数字就压在栈里,遇到符号就取出栈里的两个元素,取出的第一个为右操作数,第二个为左操作数,进行相对应的计算后再压进栈里,依次类推
举我们上文提到的例子来说:1.5 5 * 4 2 ^ 5.544 * + 4 5 + 2 * + 的计算过程为
代码实现为:
public class EvaluateSuffix {
private static double turnToDigit(String num) {
return Double.parseDouble(num);//将字符串转化为对应的double类型
}
public static double evaluateSuffix(String suffix) {
Stack<Double> stack = new Stack<>();
for (int i = 0; i < suffix.length(); i++) {
char ch = suffix.charAt(i);
if(ch == ' ') {
continue;
}//遇到空格就跳过
if(ch >= '0' && ch <= '9' ) {
StringBuffer num = new StringBuffer();
while(i < suffix.length() && ((suffix.charAt(i) >= '0' && suffix.charAt(i) <= '9') || suffix.charAt(i) == '.') ) {
num.append(suffix.charAt(i));
i++;
}
i--;//跳出循环时i++了,减回来
double tmp = turnToDigit(num.toString());
stack.push(tmp);
}else {
Double tmp1 = stack.pop();
Double tmp2 = stack.pop();
switch (ch) {
case '+': {
stack.push(tmp2 + tmp1);
break;
} case '-':{
stack.push(tmp2 - tmp1);
break;
} case '*': {
stack.push(tmp2 * tmp1);
break;
} case '/': {
stack.push(tmp2 / tmp1);
break;
} case '^': {
stack.push(Math.pow(tmp2,tmp1));
}
}
}
}
return stack.pop();//把最后的结果返回
}
}
计算的过程比较简单,仔细研究应该不难理解
4.组织:
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("输入表达式");
String infix = scanner.nextLine();
System.out.println(EvaluateSuffix.evaluateSuffix(InFixToSuffix.inFixToSuffix(infix)));
}
}
这样我们就可以在Double数据范围内进行计算,并且只考虑了正数
如果超过了这个范围,可能会出错,我们可以结合大数的加减乘除来实现,具体请等后续更新!
谢谢观看,如有漏洞,请您指出
你我都加油!!