前缀表达式(波兰表达式):
从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素和次顶元素),并将结果入栈;重复上述过程直到表达式的最左端,最后运算得出的值即为表达式的结果。
例如:(3+4)X 5-6对应的前缀表达式就是- X + 3 4 5 6 ,针对前缀表达式的求值步骤如下:
1)从右至左扫描,将6、5、4、3压入堆栈;
2)遇到 “+” 运算符,因此弹出3和4(3为栈顶元素,4为次顶元素),计算3+4的值7,将7入栈;
3)遇到 “X” 运算符,因此弹出7和5,计算7 X 5的值35,将35入栈;
4)遇到 “-” 运算符,因此弹出35和6,计算35-6的值29,由此得到最终结果29。
中缀表达式:
中缀表达式是最常见的运算表达式,如:3+4)X 5-6,中缀表达式的求值是人们最熟悉的,但是对计算机来说却不好操作(需要判断运算符的优先级)。因此,在计算结果时,往往会将中缀表达式转成其他表达式来操作。
后缀表达式(逆波兰表达式):
后缀表达式与前缀表达式相似,只是运算符位于操作数之后,例如(3+4)X 5-6对应的后缀表达式就是3 4 + 5 X 6 - 。后缀表达式的计算过程就是从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素和栈顶元素),并将结果入栈;重复上述过程直到表达式的右端,最后运算得出的值即为表达式的结果。
例如:(3+4)X 5-6对应的后缀表达式就是3 4 + 5 X 6 - ,针对后缀表达式的求值步骤如下:
1)从右至左扫描,将3和4压入堆栈;
2)遇到 “+” 运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算3+4的值7,将7入栈;
3)遇到 5 ,将 5 入栈;
4)遇到 “X” 运算符,因此弹出5和7,计算出 7 X 5 = 35,将35入栈;
5)遇到 6 ,将 6 入栈;
6)遇到 “-” 运算符,计算出 35 - 6 = 29,29即为最终的计算结果。
代码实现:
package stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
//先定义逆波兰表达式
//(3+4)X 5-6 ==> 3 4 + 5 X 6 -
String suffixExpression = "30 4 + 57 * 6 -";
//思路
//1. 先将 "3 4 + 5 * 6" 放到 ArrayList
//2. 将 ArrayList 传递给一个方法,遍历 ArrayList 配合栈进行计算
List<String> rpnList = getListString(suffixExpression);
System.out.println(rpnList);
System.out.println("计算结果是:" + calculate(rpnList));
}
//将一个逆波兰表达式,依次将数据和运算符放到 ArrayList中
public static List<String> getListString(String suffixExpression){
//将suffixExpression分割
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<>();
for (String ele:split){
list.add(ele);
}
return list;
}
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 res = 0;
if (item.equals("+")){
res = num1 + num2;
}else if (item.equals("-")){
res = num1 - num2;
}else if (item.equals("*")){
res = num1 * num2;
}else if (item.equals("/")){
res = num1 / num2;
}else {
throw new RuntimeException("运算符异常!");
}
//将 res 入栈
stack.push(res + "");
}
}
//最终留在stack中的数据就是运算结果
return Integer.parseInt(stack.pop());
}
}
中缀表达式转后缀表达式:
1)初始化两个栈:运算符栈 s1 和储存中间计算结果的栈 s2;
2)从左至右扫描中缀表达式;
3)遇到操作数时,将其压入 s2;
4)遇到运算符时,比较其与 s1 栈顶运算符的优先级:
4.1 如果 s1 为空,或栈顶运算符为左括号 “( ” ,则直接将此运算符入栈;
4.2 否则,若此运算符的优先级比栈顶运算符高,则将此运算符压入 s1;
4.3否则,将 s1 栈顶的运算符弹出并压入到 s2 中,然后转到 4.2,与新的栈顶 运算符进行比较 ;
5) 若遇到括号时:
5.1 如果是左括号 “( ”,则直接压入 s1;
5.2 如果是右括号 “ )”,则依次弹出 s1 栈顶的运算符,并压入 s2 ,直到遇到左括号为止, 此时,将这一对括号丢弃;
6)重复步骤 2~5 ,直到表达式的最右边;
7)将 s1 中剩余的运算符依次弹出并压入 s2;
8)依次弹出 s2 中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式。
package stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class infixToSuffix {
public static void main(String[] args) {
//完成将一个中缀表达式转成后缀表达式的功能
//说明
//1. 1+((2+3)*4)-5 ==> 1 2 3 + 4 * 5 -
//2. 因为直接对 String 进行操作不方便,因此先将 “1+((2+3)*4)-5” ==> 中缀表达式对应的List
//即“1+((2+3)*4)-5” ==> ArrayList [1,+,(,(,2,+,3),*,-,5]
//3. 将得到的中缀表达式List ==> 后缀表达式List
//即 ArrayList [1,+,(,(,2,+,3),*,-,5] ==> ArrayList[1,2,3,+,4,*,+,5,-]
String expression = "1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(expression);
System.out.println("对应的中缀表达式List:"+infixExpressionList);
System.out.println();
List<String> parseSuffixExpressionList = parseSuffixExpressionList(infixExpressionList);
System.out.println("对应的后缀表达式List:"+parseSuffixExpressionList);
}
//将中缀表达式转换成对应的List
public static List<String > toInfixExpressionList(String s){
//定义一个List,存放中缀表达式对应的内容
List<String > ls = new ArrayList<>();
int index = 0;//定义一个索引,用于遍历中缀表达式字符串
String str;//用于做多位数的拼接
char ch;//每遍历一个字符,就放入ch
do{
//如果ch是一个非数字,加入到ls
if ((ch = s.charAt(index)) < 48 || (ch = s.charAt(index)) < 57){
ls.add(""+ch);
index++;
}else {
//如果ch是一个数,需要考虑多位数
str = "";//现将str置为空串 '0'[48] -> '9'[57]
while (index < s.length() && (ch = s.charAt(index)) >= 48 && (ch = s.charAt(index)) <= 57){
str += ch;
index++;
}
ls.add(str);
}
}while (index < s.length());
return ls;
}
//将得到的中缀表达式List ==> 后缀表达式List
public static List<String> parseSuffixExpressionList(List<String> ls){
//定义两个栈
Stack<String> s1 = new Stack<>();
//说明:s2在整个操作中没有pop操作,后面还需逆序输出
//因此用栈比较麻烦,就不采用Stack<String> 直接用List<String> s2
//Stack<String> s2 = new Stack<>();
List<String> s2 = new ArrayList<>();//储存中间结果的List2
//遍历ls
for (String item:ls){
//如果是一个数,加入s2
if (item.matches("\\d+")){
s2.add(item);
}else if (item.equals("(")){
s1.push(item);
}else if (item.equals(")")){
//如果是右括号 “ )”,则依次弹出 s1栈顶的运算符,并压入 s2 ,直到遇到左括号为止,此时,将这一对括号丢弃;
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();//将“)”弹出,消除小括号
}else{
//当item优先级小于等于栈顶运算符优先级,需编写一个类返回运算符对应优先级
//将 s1 栈顶的运算符弹出并压入到 s2中,然后转到 4.2,与s1新的栈顶运算符进行比较
while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
s2.add(s1.pop());
}
//还需要讲item压入栈s1
s1.push(item);
}
}
while (!s1.isEmpty()){
s2.add(s1.pop());
}
return s2;//因为是存放到List,因此按顺序输出是对应的后缀表达式
}
}
//编写一个类Operation 返回运算符对应的优先级
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 res =0;
switch (operation){
case "+":
res = ADD;
break;
case "-":
res = SUB;
break;
case "*":
res = MUL;
break;
case "/":
res = DIV;
break;
default: ;
break;
}
return res;
}
}