前缀表达式:波兰表达式(运算符位于数字之前)
(3+4)×5-6的前缀表达式是 : -×+3456
针对前缀表达式求值结果:
- 从右至左扫描,将6、5、4、3压入堆栈
- 遇到+运算符,弹出3和4(3为栈顶元素、4为次顶元素),计算3+4=7,将7压入堆栈
- 接下来是×运算符,弹出7和5,计算7×5=35,将35入栈
- 最后是-运算符,计算出35(次顶)-6(栈顶)的值为29
中缀表达式:如(3+4)×5-6
计算机不好操作中缀表达式,常将中缀表达式变成后缀表达式
(3+4)×5-6的后缀表达式是 : 3、4+5×6-
针对后缀表达式求值结果:
- 从左至右扫描,将3和4压入堆栈
- 遇到+运算符,弹出3和4(3为栈顶元素、4为次顶元素),计算3+4=7,将7压入堆栈
- 将5入栈
- 接下来是×运算符,弹出7和5,计算7×5=35,将35入栈
- 将6入栈
- 最后是-运算符,计算出35(栈顶)-6(次顶)的值为29
**逆波兰计算器:**输入逆波兰表达式,使用栈实现
中缀表达式转后缀表达式步骤:
-
初始化两个栈:运算符栈s1,和储存中间结果的栈s2
-
从左至右扫描中缀表达式
-
遇到操作数压入s2
-
遇到运算符,比较与s1栈顶运算符的优先级
- 如果s1为空,或栈顶运算符为”(“,则直接将此运算符入栈
- 否则,若运算符优先级比s1栈顶高,也将运算符压入s1
- 否则,将s1栈顶的运算符弹出并压入s2中,再次转到4.1与s1新的栈顶元素比较
-
遇到括号时:
- 如果是”(“,则直接压入s1
- 如果是”)“,则依次弹出s1栈顶的运算符,并压入s2,直到遇到”(“为止,将这一对括号丢弃
-
重复步骤2~5,直到表达式最右边
-
将s1中剩余的运算符依次弹出并压入s2
-
依次弹出s2中的元素并输出,**结果的逆序即为中缀表达式对应的后缀表达式
package dk.hello;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
//将中缀表达式1+((2+3)*4)-5转成后缀表达式1 2 3 + 4 * + 5 -
//对str操作不方便,将中缀表达式转化成ArrayList[1,+,(,(,2,+,3,),*,4,),-,5]
String expression = "1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(expression);
System.out.println(infixExpressionList);
//将得到的中缀表达式ArrayList转成后缀表达式
List<String> parseSuffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println(parseSuffixExpressionList);
//先定义逆波兰表达式,用空格隔开
String suffixExpression = "3 4 + 5 * 6 -";
//1.将sufferixExpression放到ArrayList
//2.将ArrayList传入方法,遍历配合栈运算
List<String> rpnlist = getListString(suffixExpression);
System.out.println(rpnlist);
int res = calculate(rpnlist);
System.out.println(res);
}
//将中缀表达式转成ArrayList,s=1+((2+3)*4)-5
public static List<String> toInfixExpressionList(String s){
List<String> ls = new ArrayList<String>();
int i = 0; //指针用于遍历中缀表达式字符串
String str; //对多位数拼接
char c; //每遍历一个字符就放到c
do {
if ((c=s.charAt(i)) < 48 || (c=s.charAt(i)) > 57) { //非数字
ls.add("" + c);
i++; //i需要后移
}else { //如果是数字需要考虑多位数的问题
str = ""; //先将str置为空串
while (i < s.length() && ((c=s.charAt(i)) >= 48 || (c=s.charAt(i)) <= 57)) {
str += c;
i++;
}
ls.add(str); //将数字加入数组
}
} while (i < s.length());
return ls;
}
//将ArrayList[1,+,(,(,2,+,3,),*,4,),-,5]转成ArrayList[1 2 3 + 4 * + 5 -]
public static List<String> parseSuffixExpressionList(List<String> ls){
//定义两个栈
Stack<String> s1 = new Stack<String>(); //符号栈
//因为s2栈在转化过程中没有pop操作,就不用stack,就用list
//Stack<String> s2 = new Stack<String>();
List<String> s2 = new ArrayList<String>();
//遍历ls
for (String item : ls) {
//如果是一个数就入s2栈
if (item.matches("\\d+")) { //是一个数
s2.add(item);
}else if (item.equals("(")) {
s1.push(item);
}else if (item.equals(")")) {
//如果是”)“,则依次弹出s1栈顶的运算符,并压入s2,直到遇到”(“为止,将这一对括号丢弃
while (!s1.peek().equals("(")) { //peek查看栈顶的元素
s2.add(s1.pop());
}
s1.pop();//将”(“弹出s1栈
}else {
//当item的优先级小于等于s1栈顶运算符,将s1栈顶的运算符弹出并加入s2中,再次转到4.1与s1新的栈顶元素比较
while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {
s2.add(s1.pop());
}
//还需要将item压入s1
s1.push(item);
}
}
//将s1中剩余的运算符依次弹出并压入s2
while (s1.size() != 0) {
s2.add(s1.pop());
}
//按顺序输出s2就是逆序输出栈
return s2;
}
//将逆波兰表达式依次将数据和运算符放入ArrayList
public static List<String> getListString(String suffixExpression){
String[] split = suffixExpression.split(" "); //将suffixExpression分割
List<String> list = new ArrayList<String>();
for (String ele : split) {
list.add(ele);
}
return list;
}
//完成对逆波兰表达式运算
//1. 从左至右扫描,将3和4压入堆栈
//2. 遇到+运算符,弹出3和4(3为栈顶元素、4为次顶元素),计算3+4=7,将7压入堆栈
//3. 将5入栈
//4. 接下来是×运算符,弹出7和5,计算7×5=35,将35入栈
//5. 将6入栈
//6. 最后是-运算符,计算出35(栈顶)-6(次顶)的值为29
public static int calculate(List<String> ls) {
//创建栈
Stack<String> stack = new Stack<String>();
for (String item : ls) {
//使用正则表达式取出数
if (item.matches("//d+")) { //匹配的是多位数直接入栈
stack.push(item);
}else { //是符号
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (item.equals("+")) {
res = num1 + num2;
}else if (item.equals("-")) {
res = num1 - num2;
}else if (item.equals("*")) {
res = num1 * num2;
}else if (item.equals("/")) {
res = num1 / num2;
}else {
throw new RuntimeException("运算符有误");
}
//把res入栈
stack.push(""+res); //将整数转成字符串
}
}
//最后留在栈里面的就是运算结果
return Integer.parseInt(stack.pop());
}
}
//编写一个类,返回运算符优先级
class Operation {
private static int ADD = 1; //+
private static int SUB = 1; //-
private static int MUL = 2; //*
private static int DIV = 2; //除
//写一个方法,返回对应优先级数字
public static int getValue(String operation) {
int res = 0; // 存放计算结果
switch (operation) {
case "*":
res = MUL;
break;
case "/":
res = DIV;
break;
case "+":
res = ADD;
break;
case "-":
res = SUB;
break;
default:
System.out.println("不能存在该运算符");
break;
}
return res;
}
}