问题背景:使用栈来模拟表达式计算,比如计算字符串String data = "13+10*3-2"的值。
使用栈完成表达式计算的思路分析:
- 遍历表达式,将字符串按照字符数组来操作;
- 如果遍历到的字符是数字,就直接压入数字栈中;
- 如果遍历到的字符是符号,判断操作符栈是否为空;
- 如果操作符栈为空:直接将该字符压入操作符栈中;
- 如果操作符栈不为空,那么判断操作符的优先级(自定义优先级);
- 如果操作符的优先级比当前栈顶元素的优先级高,直接压入操作符栈;
- 如果操作符的优先级比当前栈顶元素的优先级低,那么数字栈中弹出两个数字,同时操作符栈弹出一个栈顶元素,从而组合成一个计算式,将计算式的结果压入数字栈中,注意此时的操作符压入操作符栈;
- 遍历完所有的字符之后,按照顺序从数字栈和操作符栈弹出对应的数和符号,计算出结果并压入数字栈,直到数字栈中只有一个数字,该数字就是表达式的最终结果。
- 注意:2*3和2*13的区别,连续的数字字符的处理,通过拼接字符串来处理;
// 判断是否是运算符
public boolean isOper(int oper){
return oper == '*' || oper == '/' || oper == '+' ||oper == '-';
}
// 定义运算符的优先级
public int priority(int oper){
if (oper == '*' || oper == '/') {
return 1;
}else if (oper == '+' || oper == '-'){
return 0;
}else {
return -1;
}
}
// 定义运算规则
public int calculator(int oper,int num1,int num2){
int res = 0; // 存放计算结果
if (oper == '+') {
res = num1+num2;
}else if (oper == '-') {
res = num2-num1; //注意这里是num2-num1,因为出栈先入后出
}else if (oper == '*') {
res = num1*num2;
}else if (oper == '/') {
res = num2/num1;
}
return res;
}
其他都是常规的入栈、出栈操作,此处就不分开列举了;
public class Calculator {
public static void main(String[] args) {
ArrayStack numStack = new ArrayStack(10);
ArrayStack operStack = new ArrayStack(10);
int num1 = 0;
int num2 = 0;
String moreNum = ""; //处理多位数
int oper = 0;
int result = 0;
String data = "13+12*3-2";// String data = "3+2*12-2"; // 多位数出现问题
char[] chars = data.toCharArray();
for (int i=0; i<chars.length; i++) { // 这里不要用foreach,涉及到index操作
// 判断是操作符,压入操作符栈
if (operStack.isOper(chars[i])) {
// 栈中没有操作符
if (operStack.isEmpty()){
operStack.push(chars[i]); // 整数和单个字符是通用的
}else if (operStack.priority(chars[i])<=operStack.priority(operStack.peek())){// 栈中有操作符,比较优先级,乘、除
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
result = numStack.calculator(oper,num1,num2);
// 将两个数的运算结果再次压入栈中
numStack.push(result);
// 当前运算符入栈
operStack.push(chars[i]);
}else if (operStack.priority(chars[i])>operStack.priority(operStack.peek())){// 加、减
operStack.push(chars[i]);
}
}else {
/*出现的问题:两位数以上,只有第一位被压入numStack,比如13,只压入1
// 判断是数字,压入运算符栈
numStack.push(ch-48); //ASCII码转换
// 比如0在ASCII中为48(数字从48开始),char-48得到真正的0
*/
moreNum +=chars[i];
// 判断当前位的下一位是否是数字(非运算符)
if (i == chars.length-1){
numStack.push(Integer.parseInt(moreNum));
}else{
if(operStack.isOper(chars[i+1])){
numStack.push(Integer.parseInt(moreNum));
moreNum = ""; // 防止出现多个多位数
}
}
}
}
// 此时栈中:numStack[2,6,3],operStack[-,+]
while (true){
if (operStack.isEmpty()){
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
result = numStack.calculator(oper,num1,num2);
numStack.push(result);
}
// 栈中的最后一个元素就是运算结果
result = numStack.pop();
System.out.println(result);
}
}
// 先创建一个栈
class ArrayStack{
private int maxSize;
private int[] stack;
private int top = -1;
public ArrayStack(int maxSize){
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
public boolean isFull(){
return top == maxSize-1;
}
public boolean isEmpty(){
return top == -1;
}
// 1.入栈
public void push(int value){
if (isFull()){
System.out.println("栈满,无法执行入栈操作");
return;
}
top++;
stack[top] = value;
}
// 2.出栈
public int pop(){
if (isEmpty()){
throw new RuntimeException("栈空,无法执行出栈操作");
}
int data = stack[top];
top--;
return data;
}
// 3.显示栈中元素
public void show(){
if (isEmpty()){
System.out.println("栈空,没有元素显示");
}
for (int i = top; i>=0 ; i--) {
System.out.printf("Stack[%d]=%s\n",i,stack[i]); // 打印的是ASCII码值
}
}
// 获取当前栈顶元素
public int peek(){
return stack[top];
}
// 判断是否是运算符
public boolean isOper(int oper){
return oper == '*' || oper == '/' || oper == '+' ||oper == '-';
}
// 定义运算符的优先级
public int priority(int oper){
if (oper == '*' || oper == '/') {
return 1;
}else if (oper == '+' || oper == '-'){
return 0;
}else {
return -1;
}
}
// 定义运算规则
public int calculator(int oper,int num1,int num2){
int res = 0; // 存放计算结果
if (oper == '+') {
res = num1+num2;
}else if (oper == '-') {
res = num2-num1; //注意这里是num2-num1,因为出栈先入后出
}else if (oper == '*') {
res = num1*num2;
}else if (oper == '/') {
res = num2/num1;
}
return res;
}
}