前缀、中缀、后缀表达式(逆波兰表达式)举例说明
运算表达式:(3+4)*5-6
前缀表达式:- * + 3 4 5 6(从右向左扫描)
中缀表达式:(3+4)*5-6 (我们人常见的运算表达式)
后缀表达式:3 4 + 5 * 6 -(从左向右扫描,与前缀表达式相反)
中缀表达式转为后缀表达式的思路
1)初始化两个栈:运算符栈s1和储存中间结果的栈s2
2)从左到右扫描中缀表达式
3)遇到数字时,将其压入s2
4)遇到运算符时,比较其与s1栈顶运算符的优先级
1.如果s1为空,或栈顶运算符为“(”,则直接将此运算符入栈
2.若此运算符优先级比栈顶运算符高,也将运算符压入s1
3.否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4)中与s1中新的栈顶的运算符比较
5)遇到括号时,
1.如果是左括号“(”,则直接压入s1
2.如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止
3.将这一对括号舍弃
6)重复步骤2至5,知道表达式的最右边
7)将s1中剩余的运算符依次弹出并压入s2
8)依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
代码实现
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
//将中缀表达式转成对应的list集合
public static List<String> toList(String expression){
//创建一个list,存放中缀表达式 对应的内容
List<String> list=new ArrayList<>();
int i=0;//相当于一个指针,用于遍历中缀表达式字符串
String str;//对多位数进行拼接
char c;//每遍历到一个字符,就放入到c中
do{
//根据ASCIL码,数字在[48,57]之间
//如果c是个非数字,则添加到list中
if((c=expression.charAt(i))<48||(c=expression.charAt(i))>57){
list.add(c+"");
i++;//i往后移
}else{
//如果是位多位数,需要考虑字符串拼接
str="";//重置
//需要考虑是否为最后一个数字
//防止越界问题
while(i<expression.length()&&(c=expression.charAt(i))>=48&&(c=expression.charAt(i))<=57){
str+=c;
i++;
}
list.add(str);
}
}while(i<expression.length());
return list;//返回list集合
}
//将中缀表达式对应的list转换为后缀表达式对应的list
public static List<String> transformList(List<String> list) {
//定义两个栈
Stack<String> s1 = new Stack<>();//符号栈
//由于s2这个栈,在整个过程中,没有弹栈(pop)操作,而且最后还要逆序输出
//比较麻烦,因此这里不使用栈Stack,而使用集合List<String> s2
List<String> s2 = new Stack<>();//储存中间结果
//遍历 list
for (String element : list) {
//如果是一个数,则添加到s2中
//运用正则表达式
if (element.matches("\\d+")) {
s2.add(element);
} else if (element.equals("(")) {
s1.push(element);
} else if (element.equals(")")) {
//依次弹出s1栈顶的运算符,并压入s2中,直到遇到左括号为止
//peek()方法 查看栈顶的元素
while (!(s1.peek().equals("("))) {
s2.add(s1.pop());
}
s1.pop();//把左括号“(”弹出
} else {
//当element运算符的优先级<=s1栈顶运算符,将s1栈顶的运算符弹出并添加到s2中
//然后再与s1中新的栈顶运算符比较
while(s1.size()!=0&&Operation.getValue(s1.peek())>=Operation.getValue(element)){
s2.add(s1.pop());
}
//之后将element压入栈
s1.push(element);
}
}
//将s1中剩余的运算符依次弹出并添加到s2中
while(s1.size()!=0){
s2.add(s1.pop());
}
//由于存放在list集合中,因此按顺序输出就是对应的后缀表达式
return s2;
}
}
//编写一个类,可以返回一个运算符对应的优先级
class Operation{
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 operation){
int result=0;
switch(operation){
case"+":
result=ADD;
break;
case"-":
result=SUB;
break;
case"*":
result=MUL;
break;
case"/":
result=DIV;
break;
case"(":
result=0;
break;
default:
System.out.println("不存在该运算符");
break;
}
return result;
}
}
后缀表达式实现计算器
思路分析
例如:2*(3+4)5-6 对应的后缀表达式为 2 3 4 + * 5 * 6 -;后缀表达式求值步骤如下:
1)从左到右扫描,将2、3以及4压入栈中
2)遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7压入栈
3)遇到 * 运算符,因此弹出7和2,计算出27的值,得14,再将14压入栈
4)将5压入栈
5)遇到 * 运算符,因此弹出5和14,计算出14*5=70,将70压入栈
6)将6压入栈
7)最后是-运算符,计算出70-6=64,则64为最终结果
注意
减法和除法一定要仔细点,要知道次顶元素为被减数(被除数),栈顶元素为减数(除数)
代码实现
//实现对后缀表达式(逆波兰表达式)的运算
public static int calculate(List<String> list){
//创建一个栈
Stack<String> stack=new Stack<>();
//遍历list
for(String element:list){
//使用正则表达式,匹配的是多位数,一位数也行
//如果是数就入栈
if(element.matches("\\d+")){
stack.push(element);
}else{
//否则弹(pop)出两个数,进行运算
int num2=Integer.parseInt(stack.pop());
int num1=Integer.parseInt(stack.pop());
int result=0;
if(element.equals("+")){
result=num1+num2;
}else if(element.equals("-")){
result=num1-num2;
}else if(element.equals("*")){
result=num1*num2;
}else if(element.equals("/")){
result=num1/num2;
}else{
throw new RuntimeException("运算符有误");
}
//把result压入栈
stack.push(result+"");
}
}
//最后在stack中的数据就是运算结果
return Integer.parseInt(stack.pop());
}
}
测试代码
//测试
public static void main(String[] args) {
//String s="2*(3+4)*5-6";
String s="3*(4*5/4+(5-4))/5";
List<String> list=toList(s);
List<String> li=transformList(list);
//System.out.println(calculate(li));//64
System.out.println(calculate(li));//3
}
中缀表达式转为后缀表达式及实现基本计算器代码如下
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
//将中缀表达式转成对应的list集合
public static List<String> toList(String expression){
//创建一个list,存放中缀表达式 对应的内容
List<String> list=new ArrayList<>();
int i=0;//相当于一个指针,用于遍历中缀表达式字符串
String str;//对多位数进行拼接
char c;//每遍历到一个字符,就放入到c中
do{
//根据ASCIL码,数字在[48,57]之间
//如果c是个非数字,则添加到list中
if((c=expression.charAt(i))<48||(c=expression.charAt(i))>57){
list.add(c+"");
i++;//i往后移
}else{
//如果是位多位数,需要考虑字符串拼接
str="";//重置
//需要考虑是否为最后一个数字
//防止越界问题
while(i<expression.length()&&(c=expression.charAt(i))>=48&&(c=expression.charAt(i))<=57){
str+=c;
i++;
}
list.add(str);
}
}while(i<expression.length());
return list;//返回list集合
}
//将中缀表达式对应的list转换为后缀表达式对应的list
public static List<String> transformList(List<String> list) {
//定义两个栈
Stack<String> s1 = new Stack<>();//符号栈
//由于s2这个栈,在整个过程中,没有弹栈(pop)操作,而且最后还要逆序输出
//比较麻烦,因此这里不使用栈Stack,而使用集合List<String> s2
List<String> s2 = new Stack<>();//储存中间结果
//遍历 list
for (String element : list) {
//如果是一个数,则添加到s2中
//运用正则表达式
if (element.matches("\\d+")) {
s2.add(element);
} else if (element.equals("(")) {
s1.push(element);
} else if (element.equals(")")) {
//依次弹出s1栈顶的运算符,并压入s2中,直到遇到左括号为止
//peek()方法 查看栈顶的元素
while (!(s1.peek().equals("("))) {
s2.add(s1.pop());
}
s1.pop();//把左括号“(”弹出
} else {
//当element运算符的优先级<=s1栈顶运算符,将s1栈顶的运算符弹出并添加到s2中
//然后再与s1中新的栈顶运算符比较
while(s1.size()!=0&&Operation.getValue(s1.peek())>=Operation.getValue(element)){
s2.add(s1.pop());
}
//之后将element压入栈
s1.push(element);
}
}
//将s1中剩余的运算符依次弹出并添加到s2中
while(s1.size()!=0){
s2.add(s1.pop());
}
//由于存放在list集合中,因此按顺序输出就是对应的后缀表达式
return s2;
}
//实现对后缀表达式(逆波兰表达式)的运算
public static int calculate(List<String> list){
//创建一个栈
Stack<String> stack=new Stack<>();
//遍历list
for(String element:list){
//使用正则表达式,匹配的是多位数,一位数也行
//如果是数就入栈
if(element.matches("\\d+")){
stack.push(element);
}else{
//否则弹(pop)出两个数,进行运算
int num2=Integer.parseInt(stack.pop());
int num1=Integer.parseInt(stack.pop());
int result=0;
if(element.equals("+")){
result=num1+num2;
}else if(element.equals("-")){
result=num1-num2;
}else if(element.equals("*")){
result=num1*num2;
}else if(element.equals("/")){
result=num1/num2;
}else{
throw new RuntimeException("运算符有误");
}
//把result压入栈
stack.push(result+"");
}
}
//最后在stack中的数据就是运算结果
return Integer.parseInt(stack.pop());
}
}
//编写一个类,可以返回一个运算符对应的优先级
class Operation{
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 operation){
int result=0;
switch(operation){
case"+":
result=ADD;
break;
case"-":
result=SUB;
break;
case"*":
result=MUL;
break;
case"/":
result=DIV;
break;
case"(":
result=0;
break;
default:
System.out.println("不存在该运算符");
break;
}
return result;
}
}