概念及计算思路
这里主要研究的是后缀表达式即逆波兰表达式
前缀表达式
前缀表达式及波兰表达式
1.前缀表达式又称波兰表达式,前缀表达式的运算符位于操作数之前
2.举例说明:(3+4)*5-6 对应的前缀表达式就是- * + 3456
前缀表达式的计算机求值
从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们做相应的计算,并将结果入栈,重复上述过程直到表达式的最左端,最后运算得出的值即为表达式的结果
例如:(3+4)5-6 对应的前缀表达式就是- * + 3456,针对前缀表达式求值的步骤如下:
1.从右到左扫描,将6、5、4、3压入堆栈
2.遇到运算符+,弹出3、4计算3+4的值,得到7在入栈
3.扫描到运算符,因此弹出7、5,运算得到35,入栈
4.最后时运算符-,计算出35-6的值,及29,得出最终结果
中缀表达式
1.中缀表达式就是最常见的运算表达式,如(3+4)*5-6
2.中缀表达式的求值是我们最熟悉的,但是对计算计来说却不好操作,因此在计算结果时,往往会将中缀表达式转成其他表达式来处理,一般是转化为后缀表达式
对于这个大家应该熟悉的吧
后缀表达式
后缀表达式(逆波兰表达式)
1.后缀表达式又称为逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后
2.(3+4)*5-6,同样用这个举例,对应的后缀表达式是 3 4 + 5 * 6 -
后缀表达式的计算机求值
从左到右扫描表达式,遇到数字压入数栈,遇到运算符时,弹出栈顶的两个数,用运算符进行计算,并将结果入栈,重复上述过程直到表达式的最右端,最后运算得出的值即为表达式的结果。
逆波兰计算器
1.先简单完成一个逆波兰表达式,使用栈,计算其结果
2.支持小括号和多位数整数,对计算器进行简化,只支持对整数的计算
3.思路分析:即上述的后缀表达式的计算求值的思路
4.但我们不能要求别人输入的是转换好的逆波兰表达式,因此还需要将中缀表达式转化为后缀表达式。
中缀表达式转换为后缀表达式
后缀表达式的确适合计算机的计算,但人还是习惯于中缀表达式,因此我们还是需要将中缀表达式啊转换为后缀表达式的
具体的步骤如下
1.初始化两个栈,运算符栈s1和存储中间结果的栈s2
2.从左至右的扫描中缀表达式
3.遇到操作数时将其压入s2
4.遇到运算符时,比较其s1栈顶运算符的优先级
1.如果s1为空,或者栈顶符号为为(,则将此运算符直接入栈
2.否则,若优先级比栈顶元素的优先级高,也将其压入s1
3.否则,将s1栈顶的运算符弹出并压入s2中,再次与运算符栈的新的栈顶元素进行比较。
5.遇到括号时
1.如果是( 直接压入s1
2.如果是 ) 依次弹出s1中的运算符,然后压入s2,直到遇到 ( 为止然后丢弃这对括号
6.重复以上的2-5的操作直到到中缀表达式的最右边结束
7.将s1中的运算符依次弹出,并压入s2
8.依次弹出s2中的元素,结果的逆序就是中缀转后缀之后的 后缀表达式
如下图例子的思路
注:真正的代码实现的时候,中间结果并不需要一定要去存入栈,他不需要去弹栈,全程只是添加进去,若是用栈去存储,最后还涉及到一个栈的逆序的问题,实现的时候用list会更方便些
废话不多说代码放上边,主函数那里自己测试的有点乱奥
这里就不用自己模拟的栈了
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class polanNotation {
public static void main(String[] args) {
//要完成将中缀表达式转为后缀表达式
//转化之前是字符串,要对字符串进行扫描
//直接对字符串进行扫描的话需要辅助指针index比较麻烦,因此这里先将字符串转化为一个中缀的list
//将中缀表达式转化为的list转化为后缀表达式的list
String exp1="(3+9)*5-60";
List<String> strings1 = parseSuffixExp(toInfixExp(exp1));
System.out.println(strings1);
System.out.println(cal((ArrayList<String>) strings1));
System.out.println("*************");
//先列出一个中缀表达式(3+4)*5-6
//将该运算表达式转换为后缀表达式为34+5*6-
//为了运算的简单我们直接在转换后的每个个体中间加上空格
String suffixExp="3 9 + 5 * 60 -";
//思路分析
//先将3 4 + 5 * 6 -放入一个arraylist中
//再将arraylist传递给一个方法,配合栈完成计算
List<String> list = getListString(suffixExp);
System.out.println(list);
int cal = cal((ArrayList<String>) list);
System.out.println(cal);
System.out.println("***********************");
String exp="(2+3)*4";
List<String> strings = toInfixExp(exp);
System.out.println(strings);
}
public static List<String> getListString(String suffixExp){
String[] s = suffixExp.split(" ");
List<String> strings = new ArrayList<String>();
for (String s1 : s) {
strings.add(s1);
}
return strings;
}
public static int cal(ArrayList<String> list){
//创建栈只需要一个栈即可。
Stack<String> strings = new Stack<String>();
for (String s : list) {
if(s.matches("\\d+")){
strings.push(s);
}else{
int num1=Integer.parseInt(strings.pop());
int num2=Integer.parseInt(strings.pop());
int res=0;
if(s.equals("+")){
res=num1+num2;
}else if(s.equals("*")){
res=num1*num2;
}else if(s.equals("+")){
res=num1+num2;
}else if(s.equals("-")){
res=num2-num1;
}else{
throw new RuntimeException("符号有误");
}
strings.push(""+res);
}
}
//最后留在栈中的是运算结果
return Integer.parseInt(strings.pop());
}
//将中缀表达式转成对应的list
public static List<String> toInfixExp(String exp){
ArrayList<String> strings = new ArrayList<String>();
int i=0;//这是一个指针,用来遍历中缀表达式
//用来做多位数的拼接工作
String str;
char ch;
do{
//如果是一个非数字就加入strings
if((ch=exp.charAt(i))<48 || (ch=exp.charAt(i))>57 ){
strings.add(""+ch);
i++;
}else{//如果是数字则要考虑多位数字的拼接问题
str="";
while(i<exp.length() && (ch=exp.charAt(i))>=48 && (ch=exp.charAt(i))<=57 ){
str+=ch;
i++;
}
strings.add(str);
}
}while(i<exp.length());
return strings;
}
//中缀表达式的list转化为后缀表达式的list
public static List<String> parseSuffixExp(List<String> list){
//先定义两个栈
Stack<String> s1=new Stack<String>();
//按照我们的思路分析这里应该用一个中间数字的栈
//而且这个栈从头到尾都没有说进行出栈的操作
//最后还要对遍历的栈进行逆序,这里我们可以不用这个栈,直接使用list来替代这个栈
List<String> s2 = new ArrayList<String>();//存中间数
for (String s : list) {
if(s.matches("\\d+")){
s2.add(s);
}else if(s.equals("(")){
s1.push(s);
}else if(s.equals(")")){//如果是 ) 依次弹出s1中的运算符,然后压入s2,直到遇到 ( 为止然后丢弃这对括号
while(!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();//消除小括号
}else{//若s是运算符号,优先级小于等于栈顶的优先级,s1弹栈并加入到s2中
while(!s1.isEmpty() && Operation.getValue(s1.peek())>Operation.getValue(s)){//需要一个比较优先级高低的方法
s2.add(s1.pop());
}
s1.push(s);
}
}
while (!s1.isEmpty()){
s2.add(s1.pop());
}
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 res=0;
switch(operation){
case "*":
res= MUl;
break;
case "/":
res=Div;
break;
case "+":
res=ADD;
break;
case "-":
res=SUB;
break;
default:
// System.out.println("this oper is not exist");
break;
}
return res;
}
}
还是喜欢晚上的,哈哈哈哈哈嗝