前缀表达式
Prefix expression 波兰表达式,运算符都位于操作数之前
从右向左扫描表达式,遇到数字时,将数字压入堆栈;遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的计算(栈顶元素和次顶元素),并将结果入栈,重复上述过程直到表达式的最左端
举例:(3+4)×5-6 prefix expression: -×+3456
从右向左扫描,将 6,5,4,3 压入栈
遇到 + 弹出 3、4,计算 3+4 得到7,将 7 入栈
接下来是 ×,弹出 7、5,计算 7 × 5,将 35 入栈
最后是 - , 计算 35-6,即 29,得到最终结果
中缀表达式
常见的运算表达式;人熟悉,但是对于计算器来说不好操作
后缀表达式(逆波兰表达式)
suffix expression 运算符位于操作数之后
举例
(3+4)×5-6 的后缀表达式为 3 4 + 5 × 6 -
从左向右扫描表达式,遇到数字,将数字压入堆栈,遇到运算符,弹出栈顶的两个数,用运算符对他们做相应的计算(次顶元素和栈顶元素),将结果入栈,重复上述过程直到表达式最右端,得到结果
-
从左向右扫描,将3、4压入堆栈
-
遇到 +,弹出3、4,计算 3+4,得 7,将 7 入栈
-
将 5 入栈
-
接下来是 ×运算符,弹出 5、7, 计算 5 × 7,将 35 入栈
-
将 6 入栈
-
最后是 - 运算符,计算 35 - 6,即 29,得到结果
逆波兰计算器
要求
输入一个逆波兰计算器,使用栈,计算结果
支持小括号和多位数整数
思路
- 先将后缀表达式放入 ArrayList 中
- 将 ArrayList 传递给一个方法,配合栈完成计算
代码实现
package stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
// define a suffix expression
// use space split number and oper
// (3+4)*5-6
String suffixExpression = "3 4 + 5 * 6 -";
// 1. put expression into arraylist
// 2. transfer Arraylist to a method, do calculate with stack
List<String> rpnList = getListString(suffixExpression);
System.out.println("rpnList"+rpnList);
int res = calculate(rpnList);
System.out.println("result is: " + res);
}
// put nums and opers of suffix expression into an ArrayList by order
public static List<String> getListString(String suffixExpression){
// split suffix expression
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>();
for(String ele:split){
list.add(ele);
}
return list;
}
// calculation
public static int calculate(List<String> ls){
// create one stack
Stack<String> stack = new Stack<>();
// traverse ls
for (String item : ls) {
// pick up number using regular expression
if (item.matches("\\d+")) { // match multiple digits
// push
stack.push(item);
} else {
// pop two nums and calculate, then push
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("have something wrong");
}
// push res
stack.push("" + res);
}
}
// the data left in stack is the result
return Integer.parseInt(stack.pop());
}
}
中缀表达式转后缀表达式
步骤分析
-
初始化两个栈,运算符栈 s1 和存储中间结果的栈 s2
-
从左至右扫描中缀表达式
-
遇到操作数时,将其压入 s2
-
遇到运算符时,比较其与 s1 栈顶运算符的优先级
-
如果 s1 为空,或栈顶运算符为左括号 ”(“,则直接将此运算符入栈
-
否则,若优先级比栈顶运算符的高,也将运算符压入 s1
-
否则,将 s1 栈顶的运算符弹出并压入到 s2 中,再次转到 4.1与 s1 中的新的栈顶运算符比较
-
-
遇到括号时
- 如果是左括号 “(”,则直接压入 s1
- 如果是右括号 ”)“,则依次 弹出 s1 栈顶的运算符,并压入 s2,直到遇到左括号为止,此时将这一对括号丢弃
-
重复步骤 2-5,直到表达式的最右边
-
将 s1 中剩余运算符依次弹出压入 s2
-
依次弹出 s2 中的元素并输出,结果的逆序即为中缀表达式的后缀表达式
代码实现
思路
因为直接对 string 进行操作不方便,先将中缀表达式转为对应的 list
// transfer infix expression to list
public static List<String> InfixToList(String s){
// define a list
List<String> ls = new ArrayList<>();
int i = 0; // pointer for traversing infix string
String str; // aiming to splice multiple digit
char c; // put each char into c
do{
// if c is not a num, join it to ls
if((c=s.charAt(i))<48 || (c=s.charAt(i))>57){ // range of num
ls.add(""+c);
i++;
}else{
// if c is a num, considering multi digits
str = ""; // default ""
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;
}
将得到的中缀表达式对应的 list 转换为后缀表达式对应的 list
存放中间结果的栈没有 pop 操作,并且需要逆序数处,因此在这里不使用 栈 结构,而是使用 ArrayList
返回优先级的类
// oper : return priority of a oper
class Operation{
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
// METHOD RETURN THE PRIORITY VALUE
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:
System.out.println("oper not exists");
break;
}
return result;
}
}
中缀表达式对应的 list 转为后缀表达式对应的 list
public static List<String> parseSuffixExpressionList(List<String> ls){
// define two stack
Stack<String> s1 = new Stack<>(); // stack for symbol
List<String> s2 = new ArrayList<>(); // store intermediate result
// traverse ls
for(String item:ls){
// if item is a num, add to s2
if(item.matches("\\d+")){
s2.add(item);
}else if(item.equals("(")){
s1.push(item);
}else if(item.equals(")")){
while(!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop(); // !!! pop "(" from s1
}else{
// if item's priority smaller than/ equals to s1 stack top
// pop top element from s1 and add it to s2
// lack a method comparing priority of oper
while(s1.size()!=0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
s2.add(s1.pop());
}
// push item to s1
s1.push(item);
}
}
// add opers left in s1 into s2
while(s1.size()!=0){
s2.add(s1.pop());
}
return s2; // due to using list storing data, so data print out is the correct order
}
test main()
String expression = "(3+4)*5-6";
List<String> infixEpressionList = InfixToList(expression);
System.out.println(infixEpressionList);
List<String> SuffixExpressionList = parseSuffixExpressionList(infixEpressionList);
System.out.println("infix: "+expression+" suffix: "+SuffixExpressionList);
System.out.println(calculate(SuffixExpressionList));