文章目录
一、中缀表达式转后缀表达式(逆波兰表达式)
1、思路
1. 初始化两个栈:运算符栈 s1 和存储中间结果的栈 s2;
2. 从左至右扫描中缀表达式;
3. 遇到操作数时,将其压入 s2;
4. 遇到运算符时,比较其与 s1 栈顶运算符的优先级:
1. 如果 s1 为空,或者 s1 栈顶元素为左括号 **“(”**,则直接压入s1;
2. 如果其优先级比栈顶运算符的优先级高,也将其压入 s1;
3. 如果上述两个条件都不满足,则将 s1 栈顶的元素弹出并压入 s2,再重复 前两步。
5. 遇到括号时:
1. 如果是左括号 **“(”**,则直接压入 s1;
2. 如果是右括号 **“)”**,则依次弹出 s1 栈顶的元素,并压入 s2,直到遇到左括号 **“(”** 为止,此时将这一对括号丢弃(弹栈)。
6. 重复步骤 2~5,直到表达式的最右端;
7. 将 s1 中剩余的元素依次弹出并压入 s2;
8. 依次弹出 s2 中的元素并输出,`结果的逆序即为当前中缀表达式对应的后缀表达式`
2. 示例
将中缀表达式 1 + ( ( 2 + 3 ) ) * 4 ) - 5 按照上述步骤转换为后缀表达式
3. 代码实现
1. 将中缀表达式存储在List集合中
代码如下:
/**
* @param expr 中缀表达式字符串
* @return 存储着中缀表达式截取元素的List
*/
public static List<String> toList(String expr) {
//初始化list
List<String> list = new ArrayList<>();
//扫描索引
int index = 0;
//用以存放每次扫描的元素
String s = "";
//用来拼接可能有的多位数
String ex = "";
//只要索引小于表达式长度,就一直扫描
while (index < expr.length()) {
//用s存放当前扫描到的元素,每扫描一个元素,索引index+1
s = expr.substring(index, ++index);
//正则判断当前元素是否为数字
if (s.matches("\\d")) {
//如果是数字,则拼接当前数字
ex += s;
//如果索引等于表达式长度,则表示已经扫描到表达式末尾,直接将当前数字添加进list
if (index == expr.length()) {
list.add(s);
} else {
//如果没有扫描到表达式末尾,则判断当前元素的下一个是否为数字
//如果不是数字,则直接将当前数字添加进list,并重置多位数拼接字符串ex
if (!expr.substring(index, index + 1).matches("\\d")) {
list.add(ex);
ex = "";
}
}
} else {
//如果是字符,则直接添加进list
list.add(s);
}
}
//返回存储表达式截取元素的list
return list;
}
/**
* 测试截取方法是否正确
* 测试结果:
* 当前中缀表达式为:1+((2+3)*4)-5
* 存储截取元素的list:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
*/
public static void main(String[] args) {
String expr = "1+((2+3)*4)-5";
System.out.println("当前中缀表达式为:" + expr);
List<String> list = toList(expr);
System.out.print("存储截取元素的list:");
System.out.println(list);
}
2. 判断运算符优先级
代码如下:
/**
* 判断运算符的优先级
* @param oper 运算符字符串
* @return 运算符的优先级(数值越大,优先级越高)
*/
public static int priority(String oper) {
if ("+".equals(oper) || "-".equals(oper)) {
return 0;
} else if ("*".equals(oper) || "/".equals(oper)) {
return 1;
} else {
return -1;
}
}
3. 将中缀表达式转为后缀表达式
代码如下:
/**
* @param list 存储着中缀表达式截取元素的List
* @return 当前中缀表达式转换的后缀表达式
*/
public static List<String> mid2Back(List<String> list) {
//初始化符号栈s1
Stack<String> s1 = new Stack<>();
//初始化中间结果栈s2
//因为s2在整个过程中没有出栈操作,且后面还需要元素逆序输出,所以此处采用List简化操作
List<String> s2 = new ArrayList<>();
//遍历list中的元素
for (String s : list) {
if (s.matches("\\d+")) {
//如果当前元素是数字(正则匹配多位数),则直接添加进s2
s2.add(s);
} else if ("(".equals(s)) {
//如果当前元素为左括号 ( ,则直接压入s1
s1.push(s);
} else if (")".equals(s)) {
//如果当前元素为右括号 ) ,则查看栈顶元素是否为左括号 (
while (!"(".equals(s1.peek())) {
//将s1栈顶元素出栈,并添加进s2,一直到s1栈顶元素为左括号 ( 为止
s2.add(s1.pop());
}
//将左括号 ( 出栈
s1.pop();
} else {
//如果当前元素为运算符
//判断s1是否为空且当前运算符的优先级是否高于s1栈顶元素的优先级
while (s1.size() != 0 && priority(s) <= priority(s1.peek())) {
//如果s1不为空且当前运算符的优先级不高于s1栈顶元素的优先级
//则将s1栈顶元素出栈,且添加进s2
s2.add(s1.pop());
}
//将当前运算符压入s1
s1.push(s);
}
}
//将s1中剩余元素出栈并添加进s2
while (s1.size() != 0) {
s2.add(s1.pop());
}
//返回存储后缀表达式的List(因为List是有序的,所以此处不用再逆序)
return s2;
}
/**
* 测试前面的所有方法是否正确
* 测试结果:
* 当前中缀表达式为:1+((2+3)*4)-5
* 存储截取元素的list:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
* 当前中缀表达式对应的后缀表达式为:[1, 2, 3, +, 4, *, +, 5, -]
*/
public static void main(String[] args) {
String expr = "1+((2+3)*4)-5";
System.out.println("当前中缀表达式为:" + expr);
List<String> list = toList(expr);
System.out.print("存储截取元素的list:");
System.out.println(list);
System.out.print("当前中缀表达式对应的后缀表达式为:");
List<String> list1 = mid2Back(list);
System.out.println(list1);
}
二、逆波兰计算器
能够支持多位整数进行四则运算的简易计算器
1. 思路
1. 从左至右扫描后缀表达式
2. 如果是数字,则直接压入栈
3. 如果是运算符,则弹出栈顶的两个元素与当前运算符进行运算(后弹出的元素对先弹出的元素进行运算),并将结果入栈
4. 重复上述过程直到扫描到表达式的最右端
5. 栈中最后剩下的元素即当前后缀表达式的运算结果
2. 示例
将后缀表达式 1 2 3 + 4 * + 5 - 按照上述步骤转换为后缀表达式
3. 代码实现
代码如下:
/**
* @param list 后缀表达式截取元素的list
* @return 表达式运算结果
*/
public static int calc(List<String> list) {
//初始化存放数据的栈
Stack<String> stack = new Stack<>();
//遍历list
for (String s : list) {
if (s.matches("\\d+")) {
//如果当前元素为数字(正则匹配多位数),则直接入栈
stack.push(s);
} else {
//如果当前元素为运算符
//则从栈中依次弹出两个元素
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
//用后弹出的元素(num1)对先弹出的元素(num2)进行四则运算
//再将运算结果压入栈
switch (s) {
case "+":
stack.push((num1 + num2) + "");
break;
case "-":
stack.push((num1 - num2) + "");
break;
case "*":
stack.push((num1 * num2) + "");
break;
case "/":
stack.push((num1 / num2) + "");
break;
default:
throw new RuntimeException("运算符不匹配!");
}
}
}
//最后在栈中只剩下一个元素,即为表达式的运算结果
return Integer.parseInt(stack.pop());
}
/**
* 测试前面的所有方法是否正确
* 测试结果:
* 当前中缀表达式为:1+((2+3)*4)-5
* 存储截取元素的list:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
* 当前中缀表达式对应的后缀表达式为:[1, 2, 3, +, 4, *, +, 5, -]
* 1+((2+3)*4)-5 = 16
*/
public static void main(String[] args) {
String expr = "1+((2+3)*4)-5";
System.out.println("当前中缀表达式为:" + expr);
List<String> list = toList(expr);
System.out.print("存储截取元素的list:");
System.out.println(list);
System.out.print("当前中缀表达式对应的后缀表达式为:");
List<String> list1 = mid2Back(list);
System.out.println(list1);
int result = calc(list1);
System.out.println(expr+" = "+result);
}