栈实现简单计算器二
1.中缀表达式转变成后缀表达式思路
- 首先创建一个符号栈(存放符号),一个集合(用来存储最终的后缀表达式,
- 本来是创建一个栈,但是该栈在遍历过程中只负责入栈没有出栈,并且最终的结果是后缀表达式的逆序,所以采用list更方便)
- 处理数字:当遍历中缀表达式为数字(用正则表达式匹配)直接添加到集合中
- 处理小括号和栈空:当为符号"(“或者栈为空时,直接入符号栈;当为”)",直接遍历符号栈并将
- 符号栈的符号添加到集合中,直到符号栈的栈顶为"(",循环结束在出栈栈顶"(";
- 处理运算符优先级:当将要入栈的运算符优先级比栈顶的运算符优先级低(小括号不是运算符,只是影响运算顺序),
出栈栈顶元素并添加到集合中;优先级反之或栈为空,就结束循环,之后元素再入栈 - 最后将符号栈的元素全部出栈并添加到集合
2.后缀表达式(逆波兰表达式)计算规则
从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,
弹出栈顶的两个数,用运算符对它们做相应的计算(次顶元素 和 栈顶元素),
并将结果入栈;重复上述过程直到表达式最右端,最后运算得出的值即为表达式的结果
(3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 -
从左至右扫描,将3和4压入堆栈;
遇到+运算符,因此弹出4和3(4为栈顶元素,3为次顶元素),计算出3+4的值,得7,再将7入栈;
将5入栈;
接下来是×运算符,因此弹出5和7,计算出7×5=35,将35入栈;
将6入栈;
最后是-运算符,计算出35-6的值,即29,由此得出最终结果
3.(重点)将中缀表达式转为集合存储
/**
* 将中缀表达式转换成集合存储(重点)
* @author zh
* @date 2021/7/15 20:47
* @param expression: 中缀表达式
* @return java.util.List<java.lang.String>
**/
public static List<String> expreConvertList(String expression){
/*用于存储中缀表达式*/
List<String> list=new ArrayList<>();
/*用于处理多位数*/
StringBuilder sb = new StringBuilder();
for (int i = 0; i < expression.length(); i++) {
/*获取字符串的每个字符并转换成字符串类型*/
String c = ""+expression.charAt(i);
/*用正则表达式匹配到是数字( c >=48&&c<=57)也可以用ascii表值判断是否是数字),添加到sb中*/
if (c.matches("^\\d+$")){
sb.append(c);
/*当为循环最后一次时是数字,那么就直接将sb的数字添加到集合中*/
if (i==expression.length()-1){
list.add(sb.toString());
}
}else {
/*当有连续的符号时,sb是空的,不能添加,如果不是那代表数字已经结束了,可以将sb存入集合中*/
if (sb.length()>0){
list.add(sb.toString());
}
/*直接将字符存入集合中*/
list.add(c);
/*用完sb就将sb置为空*/
sb=new StringBuilder();
}
}
return list;
}
4.获得运算符优先级
/**
* 得到运算符优先级
* @author zh
* @date 2021/7/16 8:25
* @param sign: 运算符
* @return int
**/
public static int getPriority(int sign){
if (sign=='*'||sign=='/'){
return 1;
}else if (sign == '+' || sign == '-'){
return 0;
} else {
System.out.println("不合法");
return -1;
}
}
5.中缀表达式转为后缀表达式
/*
初始化两个栈:运算符栈s1和储存中间结果的栈s2;
2)从左至右扫描中缀表达式
3)遇到操作数时,将其压s2;
4)遇到运算符时,比较其与s1栈顶运算符的优先级:
1.如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
2.否则,若优先级比栈顶运算符的高,也将运算符压入s1;
3.否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较;
5)遇到括号时:
(1)如果是左括号“(”,则直接压入s1
(2)如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
6)重复步骤2至5,直到表达式的最右边
7)将s1中剩余的运算符依次弹出并压入s2
8)依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
**/
/**
* 中缀转为后缀(重点)
* @author zh
* @date 2021/7/16 8:13
* @param expreList: 中缀表达式的集合形式
* @return java.util.List<java.lang.String>
**/
public static List<String> infixToSuffix(List<String> expreList){
/*符号栈*/
Stack<String> fu=new Stack<>();
/*存储中间结果和最终结果栈,由于在整个操作中该栈并没有出栈菜哦做,
而且最终结果也是逆序保存,所以该栈没必要可以用list代替*/
List<String> bi=new ArrayList<>();
for (String s : expreList) {
/*如果该值为数字,那么直接添加到集合中*/
if (s.matches("^\\d+$")){
bi.add(s);
/*如果该值为"(",由于运算优先级最大,直接入符号栈*/
}else if ("(".equals(s)||fu.isEmpty()){
fu.push(s);
/*如果该值为")",那就不入栈,将栈中元素出栈并添加到集合中直到栈顶为"(",
当循环结束后栈顶为"(",直接将"("出栈,这番操作去除了"()"*/
}else if(")".equals(s)){
while (!"(".equals(fu.peek())){
bi.add( fu.pop());
}
/*这时候栈顶是(,直接出栈*/
fu.pop();
}else {
/*当fu栈为空或fu栈栈顶为"("或将要入栈的运算符比栈顶运算符的优先级更高,直接入栈*/
if ( "(".equals(fu.peek())||getPriority(fu.peek().charAt(0))<getPriority(s.charAt(0))){
fu.push(s);
}else {
/*处理入栈的运算符比栈顶运算符的优先级更低或相同
* 如果入栈的运算符比栈顶运算符的优先级更低或相同并且栈顶不为"("将栈顶出栈并添加到集合中,
* 否则就直接入栈(优先级不满足或则栈顶为"("),循环结束,不然就直接当栈空循环结束
**/
while (!fu.isEmpty()){
if (!"(".equals(fu.peek())&&getPriority(fu.peek().charAt(0))>=getPriority(s.charAt(0))){
bi.add(fu.pop());
}else {
break;
}
}
fu.push(s);
}
}
}
/*将符号栈的元素全部添加到集合中*/
while (!fu.isEmpty()){
bi.add(fu.pop());
}
return bi;
}
6.测试主方法
public static void main(String[] args) {
/*测试*/
System.out.println(expreConvertList("1+((211+3)*4)-5"));
/*中缀表达式的集合形式*/
List<String> infixList = expreConvertList("1+(1+6/2*(211+3)*4+2*4)-5");
/*转成后缀表达式*/
List<String> list = infixToSuffix(expreConvertList("1+(1+6/2*(211+3)*4+2*4)-5"));
/*计算后缀表达式*/
System.out.println(calculate(list));
}
7.完整代码
package Stack;
/*
* @author zh
* @ClassName : Stack.ReversePolishNotation
* @Description : 类描述
* Created by user on 2021-07-15 17:02:54
* Copyright 2020 user. All rights reserved.
* 中缀表达式--->逆波兰表达式(后缀表达式,适合计算机计算)--->计算结果
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class ReversePolishNotation {
/**
* 方法描述
* @param: [expression]
* @return: int
* @author: zh
* @date: 2021/7/15
* 将传过来的后缀表达式计算出结果
*/
public static int calculate(List<String> list){
Stack<String> stack=new Stack<>();
System.out.println(list);
for (String s : list) {
/*匹配多位数*/
if (s.matches("\\d+")){
stack.push(s);
}else {
int num1=Integer.parseInt(stack.pop());
int num2=Integer.parseInt(stack.pop());
int res=0;
switch (s){
case("+"):
res=num1+num2;break;
case("-"):
res=num2-num1;break;
case("*"):
res=num1*num2;break;
case("/"):
res=num2/num1;break;
default:
System.out.println("有误");
}
stack.push(String.valueOf(res));
}
}
return Integer.parseInt(stack.pop());
}
/**
* 将中缀表达式转换成集合存储(重点)
* @author zh
* @date 2021/7/15 20:47
* @param expression: 中缀表达式
* @return java.util.List<java.lang.String>
**/
public static List<String> expreConvertList(String expression){
/*用于存储中缀表达式*/
List<String> list=new ArrayList<>();
/*用于处理多位数*/
StringBuilder sb = new StringBuilder();
for (int i = 0; i < expression.length(); i++) {
/*获取字符串的每个字符并转换成字符串类型*/
String c = ""+expression.charAt(i);
/*用正则表达式匹配到是数字( c >=48&&c<=57)也可以用ascii表值判断是否是数字),添加到sb中*/
if (c.matches("^\\d+$")){
sb.append(c);
/*当为循环最后一次时是数字,那么就直接将sb的数字添加到集合中*/
if (i==expression.length()-1){
list.add(sb.toString());
}
}else {
/*当有连续的符号时,sb是空的,不能添加,如果不是那代表数字已经结束了,可以将sb存入集合中*/
if (sb.length()>0){
list.add(sb.toString());
}
/*直接将字符存入集合中*/
list.add(c);
/*用完sb就将sb置为空*/
sb=new StringBuilder();
}
}
return list;
}
/**
* 得到运算符优先级
* @author zh
* @date 2021/7/16 8:25
* @param sign: 运算符
* @return int
**/
public static int getPriority(int sign){
if (sign=='*'||sign=='/'){
return 1;
}else if (sign == '+' || sign == '-'){
return 0;
} else {
System.out.println("不合法");
return -1;
}
}
/*
初始化两个栈:运算符栈s1和储存中间结果的栈s2;
2)从左至右扫描中缀表达式
3)遇到操作数时,将其压s2;
4)遇到运算符时,比较其与s1栈顶运算符的优先级:
1.如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
2.否则,若优先级比栈顶运算符的高,也将运算符压入s1;
3.否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较;
5)遇到括号时:
(1)如果是左括号“(”,则直接压入s1
(2)如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
6)重复步骤2至5,直到表达式的最右边
7)将s1中剩余的运算符依次弹出并压入s2
8)依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
**/
/**
* 中缀转为后缀(重点)
* @author zh
* @date 2021/7/16 8:13
* @param expreList: 中缀表达式的集合形式
* @return java.util.List<java.lang.String>
**/
public static List<String> infixToSuffix(List<String> expreList){
/*符号栈*/
Stack<String> fu=new Stack<>();
/*存储中间结果和最终结果栈,由于在整个操作中该栈并没有出栈菜哦做,
而且最终结果也是逆序保存,所以该栈没必要可以用list代替*/
List<String> bi=new ArrayList<>();
for (String s : expreList) {
/*如果该值为数字,那么直接添加到集合中*/
if (s.matches("^\\d+$")){
bi.add(s);
/*如果该值为"(",由于运算优先级最大,直接入符号栈*/
}else if ("(".equals(s)||fu.isEmpty()){
fu.push(s);
/*如果该值为")",那就不入栈,将栈中元素出栈并添加到集合中直到栈顶为"(",
当循环结束后栈顶为"(",直接将"("出栈,这番操作去除了"()"*/
}else if(")".equals(s)){
while (!"(".equals(fu.peek())){
bi.add( fu.pop());
}
/*这时候栈顶是(,直接出栈*/
fu.pop();
}else {
/*当fu栈为空或fu栈栈顶为"("或将要入栈的运算符比栈顶运算符的优先级更高,直接入栈*/
if ( "(".equals(fu.peek())||getPriority(fu.peek().charAt(0))<getPriority(s.charAt(0))){
fu.push(s);
}else {
/*处理入栈的运算符比栈顶运算符的优先级更低或相同
* 如果入栈的运算符比栈顶运算符的优先级更低或相同并且栈顶不为"("将栈顶出栈并添加到集合中,
* 否则就直接入栈(优先级不满足或则栈顶为"("),循环结束,不然就直接当栈空循环结束
**/
while (!fu.isEmpty()){
if (!"(".equals(fu.peek())&&getPriority(fu.peek().charAt(0))>=getPriority(s.charAt(0))){
bi.add(fu.pop());
}else {
break;
}
}
fu.push(s);
}
}
}
/*将符号栈的元素全部添加到集合中*/
while (!fu.isEmpty()){
bi.add(fu.pop());
}
return bi;
}
public static void main(String[] args) {
/*测试*/
System.out.println(expreConvertList("1+((211+3)*4)-5"));
/*中缀表达式的集合形式*/
List<String> infixList = expreConvertList("1+(1+6/2*(211+3)*4+2*4)-5");
/*转成后缀表达式*/
List<String> list = infixToSuffix(expreConvertList("1+(1+6/2*(211+3)*4+2*4)-5"));
/*计算后缀表达式*/
System.out.println(calculate(list));
}
}