Polish expression& Infix expression& Reverse Polish expression& Infix expression invert to Reverse Polish expression
前缀表达式(Polish expression, 波兰表达式)
- 前缀表达式的运算符位于操作数之前 如
中缀表达式
(3+4)7-5的前缀表达式是 -+3475 - 运算过程: 从右往左扫描表达式, 将数字按5,7,4,3的顺序压入栈中, 读到运算符+时弹出两个数(3和4), 运算后, 再将结果7压入栈内, 再读到运算符时弹出两个数(77), 运算后, 再将结果49压入栈内, 再读到运算符-时弹出两个数(49-5), 运算后, 再将结果44压入栈内, 即完成运算
* 前缀表达式的运算顺序已经规划好了, 因此无需判断运算符的优先级
中缀表达式(Infix expression)
- 中缀表达式就是最常见的运算表达式 如 (3+4)*7-5. 此表达式需判断运算符的优先级
后缀表达式(Reverse Polish expression, 逆波兰表达式)
- 与前缀表达式相似, 只是运算符位于操作数后面 如
中缀表达式
(3+4)7-5的后缀表达式是 34+75- - 运算过程: 从左往右扫描表达式, 将数字3,4压入栈中, 读到运算符+时弹出两个数(3和4), 运算后, 再将结果7压入栈内, 再下一个数字7压入栈内, 再读到运算符时弹出两个数(77), 运算后, 再将结果49压入栈内, 再继续将下一个数字5压入栈内, 再读到运算符-时弹出两个数(49-5), 运算后, 再将结果44压入栈内, 即完成运算
中缀表达式(Infix expression): 实现简单计算器
- 思路分析
- 定义两个栈: 一个是数值栈, 另一个运算符栈
- 定义两个方法: 判断运算符优先级的方法和计算已入栈数值的方法
- 逐个循环扫描输入的中缀表达式, 如果是数字就入数值栈, 如果是运算符, 则需要与运算符栈的栈顶运算符比较优先级. 如果优先级高于栈顶的运算符, 则直接入栈(运算符栈), 否则, 从数值栈取2元素(数值), 在从运算符栈取栈顶元素做运算, 将运算结果再存入数值栈, 之后将当前运算符入栈(运算符栈)
- 实例代码:
/** 定义数组栈*/
class ArrayStack {
/** 栈大小*/
private int maxSize;
/** 通过该数组存放数据, 模拟栈数据结构*/
private int[] stack;
/** 栈顶的 index, 初始值为-1*/
private int top = -1;
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack = new int[maxSize];
}
/** 栈满*/
public boolean isFull() {
return top == maxSize - 1;
}
/** 栈空*/
public boolean isEmpty() {
return top == -1;
}
/** 入/压栈*/
public void push(int value) {
if (isFull()) {
System.out.println("入栈失败, 栈已满!");
return;
}
top++;
stack[top] = value;
}
/** 出/弹栈*/
public int pop() {
if (isEmpty()) {
throw new RuntimeException("出栈失败, 没有数据!");
}
int value = stack[top];
top--;
return value;
}
/** 从栈顶开始打印所有内容*/
public void list() {
if (isEmpty()) {
System.out.println("打印失败, 没有数据!");
return;
}
for (int i = top; i >= 0; i--) {
System.out.printf("stack[%d]=%d\n", i, stack[i]);
}
}
/** 查看栈顶元素内容*/
public int peek() {
return stack[top];
}
/** 运算符的优先级*/
public int operPriority(int oper) {
if (oper == '*'|| oper == '/') {
return 1;
} else if(oper == '+'|| oper == '-') {
return 0;
} else {
/** 无效的表达式*/
return -1;
}
}
/** 判断是不是一个运算符*/
public boolean isOper(char val) {
return val == '+' || val=='-' || val=='*' || val=='/';
}
/** 计算方法*/
public int cal(int num1, int num2, int oper) {
int res = 0;
switch (oper) {
case '+':
res = num1 + num2;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
}
public class CalculatorApp {
public static void main(String[] args) {
System.out.println("请输入要计算的中缀表达式: ");
Scanner in = new Scanner(System.in);
String expression = in.nextLine();
in.close();
/** 定义数值栈*/
ArrayStack numStack = new ArrayStack(10);
/** 定义运算符栈*/
ArrayStack operStack = new ArrayStack(10);
/** 表达式每个字符位索引*/
int index = 0;
/** 每次循环获取到的字符*/
char ch;
/** 计算多位数时, 用于拼接的字符串变量*/
String keepnum = "";
/** 当计算时, 从数值栈出栈的第一个数值*/
int num1 = 0;
/** 当计算时, 从数值栈出栈的第而二个数值*/
int num2 = 0;
/** 运算符 char <-> int*/
int oper = 0;
/** 计算结果*/
int res = 0;
while (true) {
/** 每次循环获取单个字符*/
ch = expression.substring(index, index + 1).charAt(0);
/** 判断是否为运算符*/
if (operStack.isOper(ch)) {
if (operStack.isEmpty()) {
/** 如果定义运算符栈为空, 直接入栈*/
operStack.push(ch);
} else {
/** 判断当前运算符(如 +)的优先级是否低于栈顶运算符(如 x), 如果低或相等通过*/
if (operStack.operPriority(ch) <= operStack.operPriority(operStack.peek())) {
/** 之前已入栈的数值与运算符取出, 开始计算*/
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = operStack.cal(num1, num2, oper);
/** 数值栈的累计值计算后, 将值重新入栈*/
numStack.push(res);
/** 当前运算符(如 +), 入栈*/
operStack.push(ch);
} else {
operStack.push(ch);
}
}
} else {
/**
* 1. 如果计算器只做单数的运算, 可以直接入栈 numStack.push(ch - 48);
* 2. 如果支持多位数, 需要往下一位判断是否为数值而不是运算符. 如果下一位是运算符就可以 numStack.push(keepnum)
* */
/** 循环拼接多位数( 如 12, 2, 6, 123)*/
keepnum += ch;
/** 如果是最后一位, 不再做下一位字符的判断*/
if (index == expression.length() - 1) {
numStack.push(Integer.parseInt(keepnum));
} else {
if (operStack.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
/** 如果下一位是运算符就入栈*/
numStack.push(Integer.parseInt(keepnum));
/** 清空数*/
keepnum = "";
}
}
}
index++;
if (index >= expression.length()) {
/** 最后位, 停止循环*/
break;
}
}
while (true) {
/** 运算符栈为空时, 则计算结束停止循环*/
if (operStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = operStack.cal(num1, num2, oper);
numStack.push(res);
}
System.out.printf("%s的结算结构为 %d\n",expression, numStack.pop());
}
}
输出:
> 请输入要计算的中缀表达式:
> 12+2*6-123
> 12+2*6-123的结算结构为 -99
后缀表达式(Reverse Polish expression, 逆波兰表达式): 实现简单计算器
- 实例代码:
public class CalculatorApp {
/** 将一个逆波兰表达式, 按各数值与运算符依次放入到 ArrayList中*/
public static List<String> getListString(String expression) {
String[] arr = expression.split(" ");
List<String> list = new ArrayList<>(arr.length);
for(String e: arr) {
list.add(e);
}
return list;
}
/**
* 逆波兰表达式的运算方法:
* 1. 从左往右扫描表达式, 将数字3,4压入栈中
* 2. 读到运算符+时弹出两个数(3和4), 运算后, 再将结果7压入栈内
* 3. 再下一个数字5压入栈内
* 4. 再读到运算符*时弹出两个数(7*5), 运算后, 再将结果35压入栈内
* 5. 再继续将下一个数字6压入栈内
* 6. 再读到运算符-时弹出两个数(35-6), 运算后, 再将结果29压入栈内, 即完成运算
* */
public static int cal(List<String> expressionList) {
Stack<String> stack = new Stack<>();
for (String exp : expressionList) {
/** 是否为数值*/
if (exp.matches("\\d+")) {
/** 数值直接入栈*/
stack.push(exp);
} else {
/** 弹出两个数, 并运算*/
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int res = 0;
if (exp.equals("+")) {
res = num1 + num2;
} else if (exp.equals("-")) {
res = num2 - num1;
} else if (exp.equals("*")) {
res = num1 * num2;
} else if (exp.equals("/")) {
res = num2 / num1;
} else {
throw new RuntimeException("运算符错误!");
}
/** 每次运算结果压入栈*/
stack.push(String.valueOf(res));
}
}
/** 最后留在栈中的数值为运算结果*/
return Integer.parseInt(stack.pop());
}
public static void main(String[] args) {
/**
* 中缀表达式 (3+4)*5-6的后缀表达式为 34+5*6-
* */
String expression = "3 4 + 5 * 6 -";
List<String> expressionList = getListString(expression);
int res = cal(expressionList);
System.out.println("34+5*6-的结算结构为 " + res);
}
}
输出:
> 34+5*6-的结算结构为 29
中缀表达式转换后缀表达式(Infix expression invert to Reverse Polish expression)
- 初始化两个栈: 运算符栈 s1和存储中间结果的 List s2
- 从左到右扫描中缀表达式
- 遇到操作数时, 将其直接加到 s2
- 遇到运算符时, 将其与 s1栈顶运算符比较优先级:
(1) 如果 s1为空或栈顶运算符为左括号"(", 则直接将此运算符入栈
(2) 否则, 若优先级比栈顶的运算符高, 也将运算符压入 s1
(3) 否则, 将 s1栈顶的运算符弹出, 并压入到 s2中, 再次转到(4-1)与 s1中新的栈顶运算符相比较 - 遇到括号时:
(1) 如果是左括号"(", 则直接压入 s1
(2) 如果是右括号")", 则依次弹出 s2栈顶的运算符, 并压入 s2, 直到遇到左括号为止, 再将这一对括号丢弃 - 重复步骤2至5, 直到表达式的最右边
- 将 s1中剩余的运算符依次弹出并压入 s2
- 输出 s2
- 实例代码:
public class InfixToReversePolishApp {
/** 运算符的优先级*/
public static int operPriority(String oper) {
if (oper.equals("*") || oper.equals("/")) {
return 2;
} else if(oper.equals("+") || oper.equals("-")) {
return 1;
} else {
if (!oper.equals("(") && !oper.equals(")")) {
/** 无效的表达式*/
System.out.println("不存在该运算符" + oper);
}
return 0;
}
}
/** 中缀表达式对应的 String List*/
public static List<String> toInfixStringList(String infixExpression) {
List<String> list = new ArrayList<>();
/** 遍历中缀表达式字符串的索引*/
int index = 0;
/** 计算多位数时, 用于拼接的字符串变量*/
String keepnum;
/** 每次循环获取到的字符*/
char ch;
while (index < infixExpression.length()) {
/**
* 不是数字, 就直接加到 List
* */
if ((ch = infixExpression.charAt(index)) < 48 || (ch = infixExpression.charAt(index)) > 57) {
list.add(String.valueOf(ch));
index++;
} else {
/** 初始化多位数拼接变量*/
keepnum = "";
/**
* - 不可以为最后数(由于内部循环拼接, 需要做此判断)
* - 每个字符必须为数字 ASCII: 48~57
* */
while (index < infixExpression.length() && (ch = infixExpression.charAt(index)) >= 48
&& (ch=infixExpression.charAt(index)) <= 57) {
/** 循环拼接多位数*/
keepnum += ch;
index++;
}
list.add(keepnum);
}
}
return list;
}
/** 中缀表达式 List转换后缀表达式(逆波兰表达式) List*/
public static String infixToReversePolish(List<String> infixList) {
/** 运算符栈(含括号)*/
Stack<String> s1 = new Stack<>();
/** 存转换后的后缀表达式, 只存无取, 直到方法结束返回. 注: 返回时需逆序输出*/
List<String> s2 = new ArrayList<>();
for(String exp: infixList) {
/** 数字直接存入 s2*/
if(exp.matches("\\d+")) {
s2.add(exp);
} else if (exp.equals("(")) {
s1.push(exp);
} else if (exp.equals(")")) {
/** 如果是右括号")", 则依次弹出 s1栈顶的运算符, 并压入 s2, 直到遇到左括号为止, 再将这一对括号丢弃*/
while(!s1.peek().equals("(")) {
s2.add(s1.pop());
}
s1.pop();
} else {
/**
* 1. s1不为空
* 2. s1的栈顶运算符(如果是括号会返回0)大于等于 当前 exp(运算符)的优先级, 便将 s1栈顶弹出栈同时入栈到 s2
* */
while(s1.size() != 0 && operPriority(s1.peek()) >= operPriority(exp) ) {
s2.add(s1.pop());
}
/** 当前 exp(运算符)入栈到 s1*/
s1.push(exp);
}
}
/** 将s1中剩余的运算符依次弹出栈, 并加到 s2*/
while(s1.size() > 0) {
s2.add(s1.pop());
}
return s2.parallelStream()
.collect(Collectors.joining());
}
public static void main(String[] args) {
/**
* - 中缀表达式 1+((2+3)*4)-5的后缀表达式为 123+4*+5–
* */
String infixExpression = "1+((2+3)*4)-5";
List<String> infixList = toInfixStringList(infixExpression);
System.out.println("中缀表达式对应的 List" + infixList);
String reversePolish = infixToReversePolish(infixList);
System.out.println("后缀表达式对应的 " + reversePolish);
}
}
输出:
> 中缀表达式对应的 List[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
> 后缀表达式对应的 123+4*+5-
如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!