刚开始我利用栈的先进后出的特点,遍历运算表达式依次得到expression的每一个字符分别按规则加入符号栈和数字栈,下面代码中继承的ArrStack类是我自己用list实现的栈,java本身有stack的类,里面有我调用的所有方法,变通一下更简单:
1.1 如果符号栈为空直接push
1.2 如果栈不为空 判断优先级 如果当前符号优先级高,直接push ,否则将数字栈的栈顶两个元素 pop,并将符号栈栈顶的符号进行运算得到的结果在push进数字栈。
2.1 如果是数字,要看后面的是数字还是表达式 即多位数的情况要予以考虑。
注意:
1、两个数进行减法运算的时候,要让栈顶元素做被减数。
2、数字的判断退出循环要记得Index–,否则会越界
public class Calculator extends ArrStack{
public Calculator() {
super(100);
}
public static void main(String[] args) {
String expression = "200*2+2*10-1";
Calculator calculator1= new Calculator();
Calculator calculator2= new Calculator();
int index = 0;
int res = 0;
char ch = ' ';
int c = 0;
String str = "";
while(true) {
//依次得到expression的每一个字符
ch = expression.substring(index, index + 1).charAt(0);
//判断 ch 是什么,并作出相应的处理
if(calculator1.Isoper(ch)) { //运算符
if(calculator1.IsEmpty()) { //符号栈为空
calculator1.push(ch); //直接push
}else { // 判断优先级
if(priorty(calculator1.pick()) <priorty(ch) ){ //当前优先级高
calculator1.push(ch); //直接push
}else {
res = calculator1.cal( calculator2.pop(), calculator2.pop(), calculator1.pop());
calculator2.push(res);
calculator1.push(ch);
}
}
}else { //如果是数字,要看后面的是数字还是表达式
if(index == expression.length() - 1) {
calculator2.push(Integer.parseInt(expression.substring(index, index + 1)));
break;
}else {
while(true) {
if(calculator1.Isoper(expression.substring(index, index + 1).charAt(0))) {
index --;
break;
}else{
str = str + expression.substring(index, index + 1);
}
index ++;
}
c = Integer.parseInt(str);
calculator2.push(c); //char --> int
str = "";
}
}
index ++;
if(index >= expression.length()) {
break;
}
}
while(!calculator1.IsEmpty()) {
res = calculator1.cal( calculator2.pop(), calculator2.pop(), calculator1.pop());
calculator2.push(res);
}
System.out.println(res);
}
//返回运算及的优先顺序,数字越大,优先级最高
public static int priorty(int i) {
if(i == '*' || i == '/') {
return 1;
}else if(i == '+' || i == '-') {
return 0;
}else {
return -1;
}
}
//判断是不是运算符
public boolean Isoper(char val) {
return val =='+' ||val =='-' || val == '*' || val =='/';
}
//计算方法
public int cal (int num1, int num2,int i) {
int res = 0;
if(i== '+') {
res =num1 + num2;
}else if(i== '-') {
res =num2 - num1;
}else if(i== '*') {
res =num1 * num2;
}else if(i== '/') {
res =num1 / num2;
}
return res;
}
}
这是我将中缀表达式转成逆波兰表达式后模拟的计算器,代码更为简单,因为计算机使用逆波兰表达式进行计算不需要太多的步骤,但大家需要理解中缀表达式和逆波兰表达式的转换过程。
举个例子吧:比如将:2*(9+6/3-5)+4转化为后缀表达式2 9 6 3 / +5 - * 4 + ]
1、任何中缀表达式都由运算数,运算符,括号(大,中,小),这三部分组成。
2、从中缀表达式的左边开始扫描(脑中自己想像的),若遇到运算数时,则直接将其输出(不压入堆栈)。
3、若遇到左括号,则将其压栈。
4、若遇到右括号,表达括号内的中缀表达式已经扫描完毕。这时需将栈顶的运算符依次弹出并输出,直至遇到左括号[左括号弹出但不输出]。
5、若遇到的是运算符:
a、如果该运算符的优先级大于栈顶运算符的优先级时,将其压栈
b、如果该运算符的优先级小于栈顶运算符的优先级时,将栈顶运算符弹出并输出,接着和新的栈顶运算 符比较,若大于,则将其压栈,若小于,继续将栈顶运算符弹出并输出…(一直递归下去,直至运算符大于栈顶云算符为止)。
6、最后一步,若扫描到中缀表达式的末尾[即扫描结束],若堆栈中还有存留的运算符依次弹出并输出即可。
肯定有一些读者还是没完全弄懂(毕竟全都是文字)。接下来,举一个例子:帮助大家消化
(1)out:2 stack:
(2)out:2 stack:*
(3)out:2 stack: * (
(4)out:2 9 stack :* (
(5)out:2 9 stack :* ( + 注:在堆栈中括号的优先级最低
(6)out:2 9 6 stack :* ( +
(7)out :2 9 6 stack :* ( + /
(8)out :2 9 6 3 stack :* ( + /
(9)out :2 9 6 3 / stack :* ( +
(10)out: 2 9 6 3 / + stack : * (
(11)out:2 9 6 3 / + stack : * ( -
(12)out : 2 9 6 3 / + 5 stack : * ( - 遇到了右括号
(13)out:2 9 6 3 / + 5 - stack:* (
(14)out:2 9 6 3 / + 5 - stack:*
(15)out:2 9 6 3 / + 5 - stack :* 括号弹出但不输出
(16)out :2 9 6 3 / + 5 - * stack : 遇到了+
(17)out:2 9 6 3 / + 5 - * stack :+
(18)out:2 9 6 3 / + 5 - * 4 stack : +
(19)out:2 9 6 3 / + 5 - * 4 + stack :
package Stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args) {
String expression = "10+((2+3)*4)-5";
//将"1+((2+3)*4)-5"转成[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]
//将[1, +, (, (, 2, +, 3, ), *, 4, ), -, 5]转成成后缀表达式[10, 2, 3, +, 4, *, +, 5, -]
List<String> list1 = expressionTolist(expression);
List<String> list2 = parsesuffixExpressionlist(list1);
System.out.println(list1 + "================");
System.out.println(list2 + "=list2");
System.out.println(caculate(list2));
}
//中缀表达式list转成后缀表达式的arrayList
public static List<String> parsesuffixExpressionlist(List<String> ls){
//定义两个栈
Stack<String> s1 = new Stack<String>(); //符号栈
//stack2这个栈没有pop操作并且最后需要逆序才能得到逆波兰表达式,所以采用arraylis代替
List<String> s2 = new ArrayList<String>();
//遍历ls 获取到每一个字符
for (String item : ls) {
if(item.matches("\\d+")) { //如果是一个数 直接加入s2
s2.add(item);
}else if(item.equals("(")) { //如果是"(", 直接压入s1
s1.push(item);
}else if(item.equals(")")) { //如果是")",则依次弹出s1栈的运算符,直到遇到左括号为止,此时将这一对括号丢弃
while(!s1.peek().equals("(")) {
s2.add(s1.pop());
}
s1.pop(); //将"("弹出
}else { //运算符 将item的优先级和s1栈顶比较
while(s1.size() != 0 && Operation.priorty(s1.peek()) >= Operation.priorty(item) ) {
s2.add(s1.pop());
}
//将 item压入栈中
s1.push(item);
}
}
//将 s1中剩余的运算符压入s2 中
while(s1.size()!= 0) {
s2.add(s1.pop());
}
return s2;//因为 s2本身是一个链表,故此按序输出就是逆波兰表达式
}
//将中缀表达式转换成list
public static List<String> expressionTolist(String str){
List<String> list = new ArrayList<String>();
int i = 0; // 遍历
String s; //多位数的拼接
do {
s = ""; // 此处一定要使 s 为空串。。。。。。。。。。。。。。。。。
//如果是一个非数字,直接加入到list
if(str.charAt(i) < 48 || str.charAt(i) > 57) {
list.add(str.charAt(i) + "");
i ++;
}else { //如果是数字考虑多位数的问题
while(i < str.length() && str.charAt(i) >= 48 && str.charAt(i) <= 57) {
s = s + str.charAt(i); //拼接
i++;
}
list.add(s);
}
}while( i < str.length());
return list;
}
public static int caculate(List<String> list) {
Stack<String> stack = new Stack<String>();
//遍历list
for (String string : list) {
if(string.matches("\\d+")) {
//入栈
stack.push(string);
}else {
//pop出两个数,进行运算,并将结果push进栈
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int res = 0;
if(string.equals("+")){
res =num1 + num2;
}else if(string.equals("-")) {
res =num2 - num1;
}else if(string.equals("*")) {
res =num1 * num2;
}else if(string.equals("/")){
res =num1 / num2;
}else {
throw new RuntimeException("运算符异常");
}
stack.push(res + "");
}
}
return Integer.parseInt(stack.pop());
}
}
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 priorty(String operation) {
int res = 0;
if(operation.equals("+") ) {
res = ADD;
}else if(operation.equals("-") ) {
res = SUB;
}else if(operation.equals("*") ) {
res = MUL;
}else if(operation.equals("/") ) {
res = DIV;
}else {
System.out.println("不存在该运算符");
}
return res;
}
}