前缀表达式(波兰表达式)
介绍
前缀表达式的运算符位与操作数之前
举例
(3+4)5-6对应的前缀表达式就是-+3456
前缀表达式的计算机求值
从右至左扫描表达式,遇到数字时,先将数字压入栈堆,遇到运算符时,弹出栈顶的两个数,用运算符对它们做相应的计算(栈顶元素和次顶元素),并将结果入栈;重复上述过程直到表达式最左端,最后运算得出的值即为表达式的结果
中缀表达式
介绍
中缀表达式就是常见的运算表达式
举例
(3+4)*5-6
中缀表达式的计算机求值
中缀表达式的求值是我们最熟悉的,但是对于计算机来说却不好操作,因此,在计算结果时,往往会将中缀表达式转成其他表达式来操作
后缀表达式(逆波兰表达式)
介绍
后缀表达式又叫逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
举例
(3+4)5-6对应的后缀表达式就是35+56-
后缀表达式的计算机求值
从左至右扫描表达式,遇到数字时,将数字压入栈堆,遇到运算符时,弹出栈顶的两个数,用运算符对它们作相应的计算(栈顶元素和次顶元素),并将结果入栈;重复上述过程直到表达式的最右端,最后运算得出的值即为表达式结果
表达式之间的相互转换
中缀表达式转为后缀表达式
步骤分析
- 初始化两个栈,运算符栈s1,存储中间结果的栈s2
- 从左至右扫描中缀表达式
- 遇到操作数时,将其压入s2
- 遇到运算符时,有以下几种情况
- 如果s1为空,或s1栈顶运算符为左括号”(“,则直接将此运算符压入s1
- 否则,若优先级比栈顶运算符的高,也将运算符直接压入s1
- 否则,将s1栈顶的运算符弹出并压入到s2中,再次跳回4.1.与s1中的栈顶运算符进行比较
- 遇到括号时
- 如果是左括号”(“,则直接压入s1
- 如果是右括号”)”,则一次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时完成了一对括号的丢弃
- 重复步骤2至5,一直到中缀表达式的最右边
- 将s1中剩余的运算符依次弹出并压入s2
- 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式所对应的后缀表达式
应用
逆波兰计算器
要求
- 输入一个逆波兰表达式(后缀表达式),使用栈(Stark),计算其结果
- 支持小括号和多位数整数,因为这里我们主要讲的是数据结构,因此计算器进行简化,值支持对整数的计算
- 思路分析
- 从左至右扫描表达式
- 遇到数字时,将数字压入栈堆
- 遇到运算符时,弹出栈顶的两个数,用运算符对它们作相应的计算(栈顶元素和次顶元素),并将结果入栈
- 重复上述过程直到表达式的最右端,最后运算得出的值即为表达式结果
- 代码实现 ```java package inversepolishcalculator;
import java.util.ArrayList; import java.util.List; import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {//先定义一个逆波兰表达式//为了方便,使用空格隔开//(3 + 4) * 5 - 6 => 3 4 + 5 * 6 -//String suffixExpression = "3 4 + 5 * 6 -";//4 * 5 - 8 + 60 + 8 / 2 => 4 5 * 8 - 60 + 8 2 / +String suffixExpression = "4 5 * 8 - 60 + 8 2 / +";//1. 先将suffixExpression放到ArrayList中List<String> rpnList = getListString(suffixExpression);System.out.println(rpnList);//2. 将ArrayList传递给一个方法,遍历ArrayList配合栈完成计算int result = calculate(rpnList);System.out.println(result);}//将一个逆波兰表达式,依次将数据和运算符放到ArrayList中public static List<String> getListString(String 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<>();//遍历lsfor (String item : ls) {//这里使用正则表达式来取出数if(item.matches("\\d+")) {//匹配多位数//入栈stack.push(item);} else {//pop出两个数,并进行计算,再入栈int number1 = Integer.parseInt(stack.pop());int number2 = Integer.parseInt(stack.pop());int result = 0;if (item.equals("+")) {result = number2 + number1;} else if (item.equals("-")) {result = number2 - number1;} else if (item.equals("*")) {result = number2 * number1;} else if (item.equals("/")) {result = number2 / number1;} else {throw new RuntimeException("运算符有误,请核对后再输入...");}//把result入栈stack.push(result + "");}}//最后留在stack中的数据就是运算结果return Integer.parseInt(stack.pop());}
}
5. 实现中缀表达式转后缀表达式,并实现该计算器```javapackage inversepolishcalculator;import java.util.ArrayList;import java.util.List;import java.util.Stack;public class PolandNotation {public static void main(String[] args) {//完成将一个中缀表达式转为后缀表达式的功能//说明://1. 1+((2+3)*4)-5 => 1 2 3 + 4 * + 5 -//2. 因为直接对str进行操作,不方便,因此,先将字符串转为一个中缀表达式的List// "1+((2+3)*4)-5" => [1,+,(,(,2,+,3,),*,4,),-,5]//3. 将得到的中缀表达式对应的List装化为后缀表达式对应的List// [1,+,(,(,2,+,3,),*,4,),-,5] => [1,2,3,+,4,*,+,5,-]String expression = "1+((2+3)*4)-5";//中缀表达式字符串 转 中缀表达式ListList<String> infixExpressionList = toInfixExpression(expression);System.out.println(infixExpressionList);//中缀表达式List 转 后缀表达式ListList<String> postfixExpressionList = parseSuffixExpressionList(infixExpressionList);System.out.println(postfixExpressionList);//计算得到结果int result = calculate(postfixExpressionList);System.out.println(result);}//将得到的中缀表达式对应的List装化为后缀表达式对应的Listpublic static List<String> parseSuffixExpressionList(List<String> ls) {//定义两个站Stack<String> s1 = new Stack<>(); //符号栈//因为s2在整个装换过程中,没有pop操作,而且最后需要逆序输出,为方便没直接使用List<String> s2来存储//Stack<String> s2 = new Stack<>(); //中间结果栈List<String> s2 = new ArrayList<>();//遍历lsfor (String item : ls) {//如果是一个数,就直接加入到s2if (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 {//当ite的优先级小于或者等于s1栈顶的运算符,就应该将s1栈顶的运算符弹出并压入到s2中,再次把item与s1中的栈顶运算符进行比较while (s1.size() != 0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)) {s2.add(s1.pop());}//还需要将item压入栈中s1.push(item);}}//最后需要将s1剩余的加入到s2中while (s1.size() != 0){s2.add(s1.pop());}return s2;}//将中缀表达式转为对应的Listpublic static List<String> toInfixExpression(String s) {//定义一个List,存放List<String> ls = new ArrayList<>();int i = 0;//这是一个指针,用于遍历中缀字符串sString str;//对多位数的拼接char c; //每遍历一个字符,就放cdo {//如果c是一个非数字,就需要加入到lsif ( (c=s.charAt(i)) < 48 || (c=s.charAt(i)) > 57 ) {ls.add(c + "");i++; //后移} else { //如果是一个数,需要考虑多位数的问题str = ""; //先将str置为空while (i < s.length() && ((c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57)) {str += c;i++;}ls.add(str);}} while (i < s.length());return ls;}//将一个逆波兰表达式,依次将数据和运算符放到ArrayList中public static List<String> getListString(String 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<>();//遍历lsfor (String item : ls) {//这里使用正则表达式来取出数if(item.matches("\\d+")) {//匹配多位数//入栈stack.push(item);} else {//pop出两个数,并进行计算,再入栈int number1 = Integer.parseInt(stack.pop());int number2 = Integer.parseInt(stack.pop());int result = 0;if (item.equals("+")) {result = number2 + number1;} else if (item.equals("-")) {result = number2 - number1;} else if (item.equals("*")) {result = number2 * number1;} else if (item.equals("/")) {result = number2 / number1;} else {throw new RuntimeException("运算符有误,请核对后再输入...");}//把result入栈stack.push(result + "");}}//最后留在stack中的数据就是运算结果return Integer.parseInt(stack.pop());}}//编写一个类,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 result = 0;switch (operation) {case "+":result = ADD;break;case "-":result = SUB;break;case "*":result = MUL;break;case "/":result = DIV;break;default:break;}return result;}}
