一、初始版本:
只能对0-9的数进行四则运算
第一次结果出错,因为在循环把表达式分割入栈时“新符号的优先级小于等于符号栈已有符号的优先级”这一步,在进行计算后只把计算结果加入数栈中,忘了把表达式中的下一个运算符加入到符号栈。
//用栈实现计算器
/**
* 思路:需要两个栈,一个数字栈,一个符号栈,分别存放数字和运算符, 通过一个游标遍历表达式,依次把数字和符号存入栈中,
* 其中,符号栈:如果栈为空,直接存入;如果栈中有了符号,需要先比较二者优先级;
* 如果新符号的优先级小于等于已有的,从数栈取出2个数,从符号栈取出一个符号,进行运算,运算结果存入数栈中; 如果新的优先级大于已有的,直接存入,
* 直到表达式遍历完成,把数和符号从栈中取出,进行运算,要注意减法和除法时的情况 难点:一个是怎么判断运算符的优先级;判断是数字还是运算符
*/
public class StackCounter {
public static void main(String[] args) {
String expresion = "3+3*6-2";
int flag = 0;// 游标
int num1 = 0;
int num2 = 0;
int oper = 0;
int res=0;
char ch = 0;
ArrStack2 numStack = new ArrStack2(10);
ArrStack2 operStack = new ArrStack2(10);
// 通过循环判断进行入栈
while (true) {
// 应该先用循环把表达式分割开
ch = expresion.substring(flag, flag + 1).charAt(0);// substring()是分割String字符串得到子字符串,参数int是分割的起点,charAt()是返回指定位置的字符
if (operStack.isOper(ch)) {// 是运算符
if (!operStack.isEmpty()) {
// 符号栈不是空,要先进行优先级判断再往里面存入
if (operStack.periority(ch) <= operStack.periority(operStack.peek())) {// 如果新符号的优先级小于等于已有的,从数栈取出2个数,从符号栈取出一个符号,进行运算,运算结果存入数栈中;
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
numStack.push(res);
operStack.push(ch);
} else {// 如果新的优先级大于已有的,直接存入
operStack.push(ch);
}
} else {
// 符号栈为空,直接存入
operStack.push(ch);
}
} else {// 是数字
numStack.push(ch - 48);// ch是字符,不是Int,需要先转换类型
}
flag++;
if (flag >= expresion.length()) {
break;
}
}
//进行计算,需要出栈
while(true) {
//如果符号栈为空,说明数栈里的数据就是最后的解
if(operStack.isEmpty()) {
break;
}
num1=numStack.pop();
num2=numStack.pop();
oper=operStack.pop();
res=numStack.cal(num1,num2,oper);
numStack.push(res);
}
System.out.println("表达式的解为:"+numStack.peek());
}
}
class ArrStack2 {
/**
* 用数组模拟栈,需要几个功能,入栈、出栈、栈满、栈空、显示 新增功能:判断是数字还是运算符;设置运算符的优先级;规定计算规则
*/
public int top = -1;// 栈顶
public int maxSize;
public int stack[];
// 构造器:初始化对象
public ArrStack2(int maxSize) {
this.maxSize = maxSize;
stack = new int[maxSize];
}
// 栈满
public boolean isFull() {
return top == maxSize - 1;
}
// 栈空
public boolean isEmpty() {
return top == -1;
}
// 入栈
public void push(int data) {
if (isFull()) {
System.out.println("栈满,无法入栈");
return;
}
top++;
stack[top] = data;
}
// 出栈
public int pop() {
if (isEmpty()) {
System.out.println("栈空,无法取出数据");
return -1;
}
int value;
value = stack[top];
top--;
return value;
}
// 显示
public void list() {
if (isEmpty()) {
System.out.println("栈空");
return;
}
while (top != -1) {
System.out.println("stack[" + top + "]" + "=" + stack[top]);
top--;
}
}
// 设置优先级,用数字表示,数字越大,优先级越高
public int periority(int oper) {
if (oper == '*' || oper == '/') {
return 1;
} else if (oper == '+' || oper == '-') {
return 0;
} else {
return -1;
}
}
// 判断是数字还是运算符
public boolean isOper(char ch) {
return ch == '*' || ch == '/' || ch == '+' || ch == '-';
}
public int cal(int num1, int num2, int oper) {
int res = 0;// 计算结果
switch (oper) {
case '+':
res = num1 + num2;
break;
case '-':
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num2 / num1;
break;
default:
break;
}
return res;
}
public int peek() {// 返回栈顶的值,但是并非是让出栈
return stack[top];
}
}
运行之后,结果正确
但是,还不能对多位数和括号进行操作,下一步进行这些操作
此外,发现当表达式中加减顺序为先减后加时,计算会出错
二、能够对多位数进行计算:定义一个字符串keepnum,然后将一中判断是数字后的入栈操作更改为:
如果一个数字后还是数字,就拼接在一起,并且通过外部的while大循环能够重复这一步骤直到最后一位。
keepnum += ch;
if (flag == expresion.length() - 1) {
numStack.push(Integer.parseInt(keepnum));
} else {
if (numStack.isOper(expresion.substring(flag + 1, flag + 2).charAt(0))){
// 是符号,那就把keepnum入栈
numStack.push(Integer.parseInt(keepnum));
keepnum = "";// keepnum必须置空消除累加
}
}
要注意的是:1.当一个多位数找完拼接后,要把keepnum置空,否则会保留上次的数据;
2.在判断下一位是不是数字之前,要先判断这个数字是不是最后一位,否则会有数组越界的错误
关于加减顺序不同导致的计算错误:通过把“+”优先级调低于“-”得到了实现,但是还有别的方法吗?
// 设置优先级,用数字表示,数字越大,优先级越高
public int periority(int oper) {
/*
if (oper == '*' || oper == '/') {
return 1;
} else if (oper == '+' || oper == '-') {
return 0;
} else {
return -1;
}
*/
if (oper == '*' || oper == '/') {
return 2;
} else if (oper == '-') {
return 1;
} else if (oper == '+') {
return 0;
} else {
return -1;
}
}