例:计算(3+4)*5-6。
一.前缀表达式(波兰表达式):
表达式为:-*+3456。
计算机求值步骤:从右向左扫描,遇到数字时压入堆栈,遇到运算符时弹出栈顶的两个数字,使用运算符进行运算(若为-则计算表达式为栈顶元素-次顶元素),并将结果入栈,一直进行到表达式最左端。
运算顺序:先按顺序将所有数字入栈,再根据表达式的计算顺序逐个运算符计算。
二.中缀表达式:
与平常顺序相同,为(3+4)*5-6,对计算机来说不好操作,需要判断计算优先级,通常会转为其他表达式。
三.后缀表达式(逆波兰表达式):
表达式为34+5*6-。
计算机求值步骤:与前缀表达式相似,运算符位于操作符之后。计算时从左向右扫描,扫描到数字则入栈,扫描到操作符则弹出栈顶的两个元素进行运算(若为-则计算表达式为次顶元素-栈顶元素),并将结果入栈,一直到表达式最右端。
特点:若出现多个数字间无操作符,则代表有小括号;数字与中缀表达式顺序一致;通常在两个数字之后会有一个操作符表示这两个数字的操作。
实例:
正常表达式 逆波兰表达式
a+b ab+
a+(b-c) abc-+
a+(b-c)*d adbc-*+
a=1+3 a13+=
四.逆波兰计算器
class PolandCalculator {
public ArrayList<String> arr;
public Stack<Integer> stack = new Stack<>();
public boolean isDigit(String sub) { // 判断该字符串是否全为数字,返回值为boolean
boolean flag = true;
for (int i = 0; i < sub.length(); i++) {
char ch = sub.charAt(i);
if (ch > 57 || ch < 48) {
flag = false;
break;
}
}
return flag;
}
public int cal(int num1, int num2, int operate) { // 定义将运算符和数pop后计算的过程,输入均为int,运算符以char类型保存,其int值为ASCII码值,输出为计算结果
int res = 0;
switch (operate) {
case '+' -> res = num2 + num1;
case '-' -> res = num2 - num1;
case '*' -> res = num2 * num1;
case '/' -> res = num2 / num1;
default -> {
}
}
return res;
}
public int calculate(String expression) { // 计算表达式的值,输入为字符串类型的表达式,各数字与运算符间使用空格分隔
arr = new ArrayList<>();
String[] str = expression.split(" ");
arr.addAll(Arrays.asList(str)); // 字符串数组转ArrayList
for (String s : arr) {
if (isDigit(s)) stack.push(Integer.parseInt(s)); // 若为数字则入栈
else { // 若为运算符则弹出两个数,进行运算后入栈
int num1 = stack.pop();
int num2 = stack.pop();
int res = cal(num1, num2, s.charAt(0));
stack.push(res);
}
}
return stack.pop();
}
}
五.中缀表达式转后缀表达式
1.步骤
-
初始化两个栈:s1存储运算符,s2存储中间结果。
-
从左至右扫描中缀表达式。
-
遇到数字时压入s2.
-
遇到运算符时,比较其与s1栈顶运算符的优先级
4.1若s1为空,或栈顶运算符为"(",则将此运算符入栈。
4.2若该运算符优先级比栈顶运算符高,则将此运算符入栈。
4.3否则,将s1栈顶运算符弹出并压入s2,回到4.1与新的栈顶运算符作比较。 -
遇到括号时,"(“直接压入s1,”)"则依次弹出s1中的运算符并压入s2,之后舍弃这对括号。
-
重复步骤2-5,直到表达式最右边。
-
将s1中剩余运算符压入s2.
-
依次弹出s2中的元素,其逆序即为后缀表达式。
2.注意:
(1)首次扫描到运算符时要么压入s1(该运算符优先级较高),要么将其他运算符弹出后压入s1(该运算符优先级较低),在扫描到优先级更低的运算符或全部扫描完成后压入s2,因此在运算的数之后。
(2)不考虑括号的情况下,"*/“会比"±"更先压入s2,且位置就在其操作数后面。
(3)括号中的数和操作符会在扫描到”)"时压入s2,比括号外更早压入,因此更早执行。
3.程序
`class PolandCalculator {
public ArrayList arr;
public Stack s1 = new Stack<>(); // 保存符号的栈
public Stack s2 = new Stack<>(); // 保存中间变量的栈
public int priority(int operate) { // 定义运算符优先级,*/大于+-
int res = -1;
switch (operate) {
case '*', '/' -> res = 2;
case '+', '-' -> res = 1;
case '(' -> res = 0;
default -> {}
}
return res;
}
public ArrayList<String> gen_expression(String expression) { // 提取中缀表达式中的各项,并转为后缀表达式
arr = new ArrayList<>();
int index = -1;
for (int i = 0; i < expression.length(); i++) {
char ch = expression.charAt(i);
if (ch >= 48 && ch <= 57) {
if (index == -1) index = i; // 前一位不是数字,这一位是数字,设置此数字的起始索引
}
else {
if (index != -1) {
arr.add(expression.substring(index, i)); // 数字结束,将其加入到ArrayList中
index = -1; // 此数字扫描结束,index设置成初值
}
if (ch == ' ') continue;;
arr.add(ch + "");
}
if (i == expression.length() - 1) arr.add(expression.substring(index, i + 1));
}
// 将表达式转为后缀表达式
for (String str : arr) {
if (isDigit(str)) {
s2.push(str); // 若是数字则直接压入s2
}
else { // 若是运算符
char ch = str.charAt(0);
if (s1.isEmpty() || ch == '(') s1.push(ch + ""); // s1为空或该符号为"("时直接压入s1
else if (ch == ')') { // 若是")"则将"("之后的所有运算符压入s2
char last_operate = s1.pop().charAt(0);
while (last_operate != '(') {
s2.push(last_operate + "");
last_operate = s1.pop().charAt(0);
}
}
else {
boolean isMin = false; // 指示当前运算符是否优先级最小
char last_operate = s1.pop().charAt(0); // 否则弹出s1栈顶的运算符
while (priority(ch) <= priority(last_operate)) { // 当前运算符优先级小于等于栈顶运算符时将栈顶运算符压入s2,再和下一个栈顶运算符比较
s2.push(last_operate + "");
if (s1.isEmpty()) {
isMin = true;
break;
}
last_operate = s1.pop().charAt(0);
}
if (!isMin) s1.push(last_operate + "");
s1.push(ch + "");
}
}
}
while (!s1.isEmpty()) s2.push(s1.pop());
arr.clear();
while (!s2.isEmpty()) arr.add(0, s2.pop());
return arr;
}
public boolean isDigit(String sub) {
boolean flag = true;
for (int i = 0; i < sub.length(); i++) {
char ch = sub.charAt(i);
if (ch > 57 || ch < 48) {
flag = false;
break;
}
}
return flag;
}
public int cal(int num1, int num2, int operate) { // 定义将运算符和数pop后计算的过程,输入均为int,输出为计算结果
int res = 0;
switch (operate) {
case '+' -> res = num2 + num1;
case '-' -> res = num2 - num1;
case '*' -> res = num2 * num1;
case '/' -> res = num2 / num1;
default -> {
}
}
return res;
}
public int calculate(String expression) {
Stack<Integer> stack = new Stack<>();
ArrayList<String> suffix_expression = gen_expression(expression);
for (String s : suffix_expression) {
if (isDigit(s)) stack.push(Integer.parseInt(s));
else {
int num1 = stack.pop();
int num2 = stack.pop();
int res = cal(num1, num2, s.charAt(0));
stack.push(res);
}
}
return stack.pop();
}
}`