逆波兰计算器的实现思路
-
定义:逆波兰表达式又叫做后缀表达式。逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasiewicz)于1929年首先提出的一种表达式的表示方法 [1] 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。
>>整体思路: 1. 首先我们输入中缀表达式; 例如:1+((2+3)*4)-5 2. 将其转换为list集合下的单个元素; 例如:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] 3. 再将该list集合转换为后缀表达式的list集合(逆波兰表达式);例如:[1, 2, 3, +, 4, *, +, 5, -] 4. 再对后缀表达式进行运算; 得到结果:16
-
页内目录
一,中缀表达式转换list集合
二,中缀表达式的list集合转后缀表达式的list集合
三,对后缀表达式进行计算
四,测试
思路的实现
一,中缀表达式转换list集合
>>思路:
1. 定义存放中缀表达式的list集合
2. 通过while循环实现逐个添加:
3. 情况一:是符号:运用方法charAt判断;并加入list集合
4. 情况二:是数字:考虑多位数的情况,并加入到list集合
5. 返回list集合
public static List<String> toInfixExpList(String s) {
//定义一个存放的集合,存放中缀表达式的各项
List<String> ls = new ArrayList<>();
int i = 0;//类似指针,用于辅助遍历中缀表达式
String str;//用于拼接多位数
do {
//如果是非数字,加入到ls中
if (s.charAt(i) < 48 || s.charAt(i) > 57) {
ls.add("" + s.charAt(i));//将该字符拼成String
i++;//i需要后移
} else {//是数
str = "";//将str置成""
while (i < s.length() && s.charAt(i) >= 48 && s.charAt(i) <= 57) {//判断该位是否为数字
str += s.charAt(i);//保证多位数的拼接
i++;//多位数的保证
}
ls.add(str);
}
} while (i < s.length());
return ls;
}
二,中缀表达式的list集合转后缀表达式的list集合
中缀list:[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
后缀list:[1, 2, 3, +, 4, *, +, 5, -]
>>思路:
1. 定义符号栈
2. 定义存放后缀表达式的集合
3. for循环遍历中缀表达式:
如果是一个数:则直接存放到集合(用正则表达式判断)
如果是左括号:则入符号栈
如果是右括号:运用while循环直到遇见左括号为止(判断是否遇见此处用栈的peek方法);
进行依次弹出栈顶的运算符,并存入集合,最后丢弃该括号
如果是运算符:此处在外写一个判断优先级的方法priority
当遍历的元素的优先级小于等于栈顶运算符优先级时,将栈的栈顶元素弹出并加入到集合
然后将遍历的元素加入到栈中
4. 此时将栈中的元素添加到集合中
5. 返回集合
public static List<String> toSuffixExpList(List<String> s) {
//分别定义符号栈和后缀表达式的各项的list集合
Stack<String> s1 = new Stack<>();//符号栈
List<String> s2 = new ArrayList<>();
//遍历s
for (String item : s) {
//如果是个数则加入s2,运用正则表达式判断
if (item.matches("\\d+")) {
s2.add(item);
} else if (item.equals("(")) {//如果是左括号
s1.push(item);//就加入到栈s1中
} else if (item.equals(")")) {//如果是右括号,则依次弹出栈顶的运算符,并加入s2,直到遇到左括号为止,并丢弃该对括号
while (!s1.peek().equals("(")) {//通过查看栈顶元素,判断是否到达)
s2.add(s1.pop());//将栈s1的栈顶元素弹出,并加入到栈s2
}
s1.pop();//弹出(
} else {//当item的优先级小于等于S1栈顶运算符优先级,将将s1中的栈顶元素弹出并加入到s2
//在本类中写一个比较优先级的方法priority
while (s1.size() != 0 && priority(s1.peek()) >= priority(item)) {//栈顶元素比item的优先级高
s2.add(s1.pop());
}
s1.add(item);//将item压入s1栈
}
}
//将s1的剩余元素加入s2
while (s1.size() != 0) {
s2.add(s1.pop());
}
return s2;
}
/**
*priority方法配合使用toSuffixExpList方法
*/
public static int priority(String item) {
int result = 0;
switch (item) {
case "+":
result = 1;
break;
case "-":
result = 1;
break;
case "*":
result = 2;
break;
case "/":
result = 2;
break;
default:
System.out.println("该运算符没有录入");
break;
}
return result;
}
三,对后缀表达式进行计算
后缀list:[1, 2, 3, +, 4, *, +, 5, -]
>>思路:(整个过程注意入栈转String,出栈要运算的数转Integer)
1. 创建一个栈用于存放计算中的结果
2. 遍历后缀表达式的元素:
如果是数字:存入栈中
如果是运算符:pop出栈的两个数根据该运算符进行不同的运算(注意弹出两个数的先后)
将计算结果入栈
3. 最后栈中元素便是计算结果
public static int calculate(List<String> ls) {
//创建一个栈
Stack<String> stack = new Stack<>();
//遍历ls
for (String item : ls) {
if (item.matches("\\d+")) {
stack.push(item);
} else {
//pop出两个数进行运算,并入栈
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int result = 0;
if (item.equals("+")) {
result = num1 + num2;
} else if (item.equals("-")) {
result = num1 - num2;
} else if (item.equals("*")) {
result = num1 * num2;
} else if (item.equals("/")) {
result = num1 / num2;
} else {
System.out.println("运算符未录入");
}
stack.push(result + "");//将计算结果变成String再加入栈中
}
}
//留下来的数据就是结果
return Integer.parseInt(stack.pop());//取出字符串类型的数字,转成数字类型
}
四,测试:
public static void main(String[] args) {
String infixExp = "1+((2+3)*4)-5";
//将中缀运算式==>>中缀表达式list集合
System.out.println(toInfixExpList(infixExp));//[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
//将中缀表达式的list集合==>>后缀表达式的list集合
System.out.println(toSuffixExpList(toInfixExpList(infixExp)));//[1, 2, 3, +, 4, *, +, 5, -]
//对后缀表达式进行计算,即逆波兰表达式的运算
System.out.println(calculate(toSuffixExpList(toInfixExpList(infixExp))));//16
}