逆波兰计算器
输入一个逆波兰表达式(后缀表达式),使用栈(Stack),计算其结果
思路如下
例如(3+4)X5-6对应的后缀表达式就是 34+5X6-,针对后缀表达式求值步骤如下:
- 从左至右扫描。将3和4压入堆栈;
- 遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈
- 将5入栈
- 接下来是X运算符,因此弹出5和7,计算出7X5 = 35,将35入栈
- 将6入栈
- 最后是-运算符,计算出35-6的值,即25,由此得出最终结果
代码如下:
public static int Calculation(ArrayList<String> list){
//创建一个栈 用来计算结果
Stack<String> stack = new Stack<>();
for (String s : list) {
if(s.matches("\\d+")){//如果数字就直接入栈
stack.push(s);
}else{
//如果是符号 就从栈中 取出栈顶和次顶的数 进行计算,再将结果入栈
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0; //存储计算的结果
if(s.equals("+")){
res = num1+num2;
}else if(s.equals("-")){
res = num1-num2;
}else if(s.equals("*")){
res = num1*num2;
}else if(s.equals("/")){
res = num1/num2;
}else{
throw new RuntimeException("符号错误");
}
//将计算结果再压入栈中
stack.push(""+res);
}
}
//循环结束后 栈中只剩一个值 那么就是最后的运算结果
return Integer.parseInt(stack.pop());
}
中缀表达式转换为后缀表达式
思路如下
- 初始化两个栈: 运算符栈s1和储存中间结果的栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级
- 如果s1为空,或栈顶运算符为左括号"(",则直接将此运算符入栈;
- 否则,若优先级比栈顶运算符的高,也将运算符压入s1;
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较
- 遇到括号时:
- 如果是左括号"(",则直接压入s1
- 如果是右括号")",则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃。
- 重复步骤2至5,直到表达式的最右边
- 将s1中剩余的运算符依次弹出并压入到s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
举例说明
将中缀表达式"1+((2+3)x4)-5"转换为后缀表达式的过程如下
结果为:“1 2 3 + 4 X + 5 -”
代码如下
package day05;
import java.util.ArrayList;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
//完成将一个中缀表达式转换成后缀表达式的功能
//说明
//1. 1+((2+3)x4)-5 =>转成1 2 3 + 4 x + 5 -
//2.因为直接对str进行操作,不方便,因此 "1+((2+3)x4)-5" ==>中缀的表达式对于的list
//3. 即"1+((2+3)x4)-5" => ArrayList [1,+,(,(,2,+,3,),*,4,),-,5]
//4. 将得到的中缀表达式对于的List => 后缀表达式对应的List
// ArrayList [1,+,(,(,2,+,3,),*,4,),-,5] => ArrayList [1,2,3,+,4,*,+,5,-]
String express = "1+((20+3)*4)-5";
ArrayList<String> list = toExpressList(express);
System.out.println("中缀表达式为"+list);
ArrayList<String> list1 = toSuffixExpressList(list);
System.out.println("后缀表达式为"+list1);
int result = Calculation(list1);
System.out.println(express + "的结果=" + result);
// //先定义逆波兰表达式
// //(3+4)x5-6 => 3 4 + 5 x 6 - =>29
// //为了方便 逆波兰表达式的数字和符号使用空格隔开
// String suffixExpression = "3 4 + 5 * 6 -";
//
// //1.我们先将字符串加入ArrayList中 方便我们进行遍历
// ArrayList<String> list = getStringList(suffixExpression);
// System.out.println(list);
//
// //2.遍历ArrayList 计算结果
// int res = Calculation(list);
// System.out.println("最后的运算结果为"+res);
}
public static ArrayList<String> toSuffixExpressList(ArrayList<String> ls){
//1.定义一个符号栈用来存储符号s1
Stack<String> s1 = new Stack<>();
//2.为了方便计算 将存储操作数 定义为ArrayList 如果是栈的话 还有进行逆序 比较麻烦
ArrayList<String> list = new ArrayList<>();
//3.遍历中缀表达式的List 根据规则转换为后缀表达式
for (String item : ls) {
//4. 如果是操作数的话 就直接加入list中
if(item.matches("\\d+")){
list.add(item);
}else if (item.equals("(")){
//5.如果是左括号的话 就直接压入s1栈中
s1.push(item);
}else if (item.equals(")")){
//6. 如果是右括号,则依次弹出s1栈顶的运算符,并加入list,直到遇到左括号为止,并将左括号从s1栈中弹出
while(!s1.peek().equals("(")){
list.add(s1.pop());
}
//7.并将左括号从s1栈中弹出
s1.pop();
}else {
//8.如果是运算符做一下的处理
if(s1.isEmpty() || s1.peek().equals("(")){
//9.如果s1栈为空 或者s1栈顶的运算符为( 则直接将此运算符入栈
s1.push(item);
}else if( (Operator.getValue(item))>(Operator.getValue(s1.peek())) ){
//10. 如果当前运算符的优先级大于s1栈顶的优先级 那么就直接将此运算符入栈
s1.push(item);
}else{
//11.当前运算符的优先级小于s1栈顶的优先级 依次从s1栈顶取值直到当前运算符的优先级大于等于栈顶的优先级
while (s1.size()>0 && (Operator.getValue(item))<=(Operator.getValue(s1.peek()))){
list.add(s1.pop());
}
//12.循环结束 就代表当前item优先级大于当前栈顶的优先级
// 就需要将这item再次压入栈中
s1.push(item);
}
}
}
//12.将s1中剩余的运算符依次加入s2中
while(s1.size()>0){
list.add(s1.pop());
}
return list;
}
//将中缀表达式字符串 转换成相应的中缀表达式list
public static ArrayList<String> toExpressList(String expression) {
//1.定义一个索引用来扫描字符串
int i = 0;
//2.定义一个char变量 用来接收每个字符串中的字符
char c = ' ';
//3.定义一个String变量, 用来处理多位数拼接的问题
String str;
//4. 创建一个ArrayList 用来接收 转换之后的结果
ArrayList<String> list = new ArrayList<>();
//5.进行循环转换
//5.1 当i小于字符串的时候 一直循环 直到i大于等于字符串结束循环
while (i < expression.length()) {
//这里运用的ASCII码进行比较的 小于48 大于57 就是非数字 大于48 小于57 就是数字
//5.2 如果字符串是非数字的时候 直接添加
if ((c = expression.charAt(i)) < 48 || (c = expression.charAt(i)) > 57) {
list.add(c + ""); //5.3将字符添加到list中
i++; //5.4 更新索引i 进行扫描下一个字符
} else { //5.5考虑多位数的问题
// 5.6 每次拼接完 都要将拼接的字符串 变为""
str = "";
//5.7如果i的长度小于 字符串的长度 并且是数字
while (i < expression.length() && (c = expression.charAt(i)) >= 48 && (c = expression.charAt(i)) <= 57) {
str += c;
i++;
}
//5.8循环结束 那么str就是多位数
list.add(str);
}
}
return list;
}
public static ArrayList<String> getStringList(String expression){
//1. 先将expression 空格进行分割
String[] split = expression.split(" ");
//2.创建ArrayList
ArrayList<String> list = new ArrayList<>();
//3. 遍历字符数组 加入ArrayList中
for(String rmp: split){
list.add(rmp);
}
return list;
}
public static int Calculation(ArrayList<String> list){
//创建一个栈 用来计算结果
Stack<String> stack = new Stack<>();
for (String s : list) {
if(s.matches("\\d+")){//如果数字就直接入栈
stack.push(s);
}else{
//如果是符号 就从栈中 取出栈顶和次顶的数 进行计算,再将结果入栈
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0; //存储计算的结果
if(s.equals("+")){
res = num1+num2;
}else if(s.equals("-")){
res = num1-num2;
}else if(s.equals("*")){
res = num1*num2;
}else if(s.equals("/")){
res = num1/num2;
}else{
throw new RuntimeException("符号错误");
}
//将计算结果再压入栈中
stack.push(""+res);
}
}
//循环结束后 栈中只剩一个值 那么就是最后的运算结果
return Integer.parseInt(stack.pop());
}
}
//定义一个比较运算符优先级的类
class Operator{
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 item){
int result = 0;
switch (item){
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.println("没有该运算符!");
}
return result;
}
}