中缀表达式转后缀表达式步骤:
1.初始化两个栈:储存运算符的栈s1和储存中间结果的栈s2;
2.从左至右扫描中缀表达式;
3.遇到操作数时,将其压入s2;
4.遇到运算符时,比较其与s1栈顶运算符的优先级:
1)如果s1为空,或栈顶运算符为左括号“(”,或优先级比栈顶运算符的高,则直接将此运算符压入s1;
2)否则,将s1栈顶的运算符弹出并压入到s2中,再次与新的s1栈顶运算符相比较。
5.遇到括号时:
1)如果是左括号“(”,则直接压入s1;
2)如果是右括号“)”,则依次弹出s1栈顶运算符并压入s2,直到遇上左括号为止,此时将这一对括号丢弃。
6.重复步骤2-5,直到表达式的最右边;
7.将s1中剩余的运算符依次弹出并压入s2;
8.依次弹出s2的元素,结果的逆序即为所对应的后缀表达式。
代码实现:
import java.util.ArrayList;
import java.util.Stack;
/*
中缀表达式转后缀表达式步骤:
1.初始化两个栈:储存运算符的栈s1和储存中间结果的栈s2;
2.从左至右扫描中缀表达式;
3.遇到操作数时,将其压入s2;
4.遇到运算符时,比较其与s1栈顶运算符的优先级:
1)如果s1为空,或栈顶运算符为左括号“(”,或优先级比栈顶运算符的高,则直接将此运算符压入s1;
2)否则,将s1栈顶的运算符弹出并压入到s2中,再次与新的s1栈顶运算符相比较。
5.遇到括号时:
1)如果是左括号“(”,则直接压入s1;
2)如果是右括号“)”,则依次弹出s1栈顶运算符并压入s2,直到遇上左括号为止,此时将这一对括号丢弃。
6.重复步骤2-5,直到表达式的最右边;
7.将s1中剩余的运算符依次弹出并压入s2;
8.依次弹出s2的元素,结果的逆序即为所对应的后缀表达式。
*/
public class HouZhuiExpressionDemo {
public static void main(String[] args) {
Stack<String> s1 = new Stack<>();
Stack<String> s2 = new Stack<>();
String expression = "1+((200+32)*4456)-512"; // 前缀表达式
ArrayList<String> list = null;
try {
list = string2ArrayList(expression); // 将字符串转换为字符列表
} catch (MyException e) {
System.out.println(e.getMessage());
}
if (list != null) {
for (String s : list) {
if (isNumber(s)) {
// 遇到操作数时,将其压入s2;
s2.push(s);
}else if (isOpera(s)) {
// 遇到运算符时,调用方法比较运算符优先级
compare(s1, s2, s);
}else {
if (s.equals("(")) {
// 如果是左括号“(”,则直接压入s1;
s1.push(s);
}else if (s.equals(")")) {
// 如果是右括号“)”,则依次弹出s1栈顶运算符并压入s2,直到遇上左括号为止。
while (!s1.peek().equals("(")) {
s2.push(s1.pop());
}
// 将括号丢弃
s1.pop();
}
}
}
}
while (!s1.isEmpty()) {
s2.push(s1.pop());
}
for (String s : s2) {
System.out.print(s + " "); // 输出:1 200 32 + 4456 * + 512 -
}
}
// 字符串转换为字符列表
public static ArrayList<String> string2ArrayList(String s) throws MyException {
String nums = ""; // 用于暂存多位数
ArrayList<String> ls = new ArrayList<>();
for (int i = 0; i < s.length(); i++) {
String str = String.valueOf(s.charAt(i));
if (isNumber(str)) {
nums += str;
// 添加最后一位数字到列表
if (i == s.length() - 1) {
ls.add(nums);
}
}else if (isOpera(str) || str.equals("(") || str.equals(")")) {
// 遍历到运算符或括号时,将之前得到的多位数加入列表,并将nums清零。
if (!nums.equals("")) {
ls.add(nums);
}
ls.add(str);
nums = "";
}else if (str.equals(" ") || str.equals("\t") || str.equals("\n")) {
// 忽略空格、制表符和换行符。
continue;
}else {
throw new MyException("请输入正确字符!");
}
}
return ls;
}
public static boolean isOpera(String s) {
return s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/");
}
public static boolean isNumber(String s) {
// 匹配多位数
return s.matches("\\d+");
}
public static int prior(String s) {
return switch (s) {
case "+", "-" -> 0;
case "*", "/" -> 1;
default -> -1;
};
}
public static void compare(Stack<String> s1, Stack<String> s2, String s) {
if (s1.isEmpty() || s1.peek().equals("(")) {
// 如果s1为空,或栈顶运算符为左括号“(”,直接将此运算符压入s1;
s1.push(s);
}else if (prior(s) > prior(s1.peek())) {
// 如果s优先级比栈顶运算符高,直接将此运算符压入s1;
s1.push(s);
}else {
// 否则,将s1栈顶的运算符弹出并压入到s2中,再次与新的s1栈顶运算符相比较。
s2.push(s1.pop());
compare(s1, s2, s);
}
}
}
使用后缀表达式进行计算:
// 使用后缀表达式进行计算:
// 只需要使用到一个栈,遇到数字时压栈,遇到运算符时弹出两个数进行运算(注意后弹栈-或者/前弹栈)
// 得到运算结果后再压栈,最后留在栈中的数字就是最终运算结果。
public static int calculator(Stack<String> stack) {
Stack<String> myStack = new Stack<>();
for (String s : stack) {
if (isNumber(s)) {
myStack.push(s);
}else if (isOpera(s)) {
int num1 = Integer.parseInt(myStack.pop());
int num2 = Integer.parseInt(myStack.pop());
switch (s) {
case "+" -> myStack.push(String.valueOf(num1 + num2));
case "-" -> myStack.push(String.valueOf(num2 - num1));
case "*" -> myStack.push(String.valueOf(num1 * num2));
case "/" -> myStack.push(String.valueOf(num2 / num1));
}
}
}
return Integer.parseInt(myStack.pop());
}