栈实现综合计算器(代码实现)
我们先给出我们的栈类的代码:
package com.ffyc.stack;
/**
* 创建一个栈,使用这个栈来实现综合计算器的运行
*/
public class ArrayStack2 {
private int maxSize; //栈的大小(也就是底层数组的大小)
private int top = -1; //栈顶指针初始化为-1
private int[] stack; //创建一个数组表示栈
public ArrayStack2(){
//默认创建大小为10的栈
this.maxSize = 10;
this.stack = new int[maxSize];
}
public ArrayStack2(int maxSize) {
this.maxSize = maxSize;
this.stack = new int[maxSize];
}
//判断栈是否满的方法
public boolean isFull(){
return top == maxSize - 1;
}
//判断栈空的方法
public boolean isEmpty(){
return top == -1;
}
//入栈操作(push)
public void push(int value){
//判断栈是否已满
if(isFull()){
System.out.println("栈已满,无法添加元素!");
return;
}
top += 1;
stack[top] = value;
}
//出栈操作(pop)
public int pop(){
//判断栈是否为空
if(isEmpty()){
//抛出一个异常
throw new RuntimeException("栈空,没有数据~~~");
}
int value = stack[top];
top -= 1;
return value;
}
//遍历栈操作
public void list(){
//判断栈是否为空
if(isEmpty()){
System.out.println("栈为空~~");
return;
}
//这里我们可以使用for循环遍历,也可以使用while循环遍历,注意: 遍历的过程中top指针一定不可以变,我们只是遍历,而不是清空栈
for (int i = top; i >= 0; i--) {
System.out.printf("stack[%d] = %d\n",i,stack[i]);
}
}
/**
* 以下就是我们编写总和计算器中会使用到的一些方法
*/
//返回运算符的优先级,优先级我们使用数字来表示 , 数字越大表示优先级就越高
public int priority(int oper){
if(oper == '*' || oper == '/'){
return 1;
}else if(oper == '+' || oper == '-'){
return 0;
}else{
return -1;
}
}
//判断是否是一个运算符
public boolean isOper(char val){
return val == '+' || val == '-' || val == '*' || val == '/';
}
//运算的方法
public int col(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];
}
}
然后给出我们的综合计算器类:
/**
* 实现我们的计算机类
*/
class Calculator{
public static void main(String[] args) {
//创建我们要执行的运算表达式
String expression = "3+2*6-2";
//创建两个栈,一个是数栈一个是符号栈
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
//定义计算运算过程中可能会使用到的变量
int index = 0; //用于对我们的运算表达式进行一个扫描
int num1 = 0; //用于接收运算符右边的数值
int num2 = 0; //用于接收运算符左边的数值
int oper = 0; //用于接收出栈的操作符
int res = 0; //用于接收每一次算数运算之后的结果
/*
这个时候注意: 由于扫描的时候我们不知道目前扫描到的数据数值还是一个运算符,但是都是通过charAt()方法获取到的,所以我么都是先使用ch变量来接收
*/
char ch = ' '; //每次都将扫描得到的char型数据保存到ch变量中
//while循环开始扫描我们的算数表达式expression
while(true){
//一次得到expression中的每一个字符
ch = expression.charAt(index);
//判断ch是一个什么,是一个数值还是一个运算符? 并执行相应的操作
if(operStack.isOper(ch)){ //由于我们是将判断是否为运算符的方法编写到了ArrayStack2类中,所以我们就要使用ArrayStack2类的实例来调用此方法判断ch是否是一个运算符
//判断符号栈是否为空,如果栈为空,这个时候就直接将我们符号加入到符号栈中
if(operStack.isEmpty()){
operStack.push(ch);
}else{
//如果符号栈中有操作符了,这个时候就要进行一个判断: 判断我们的ch和符号栈栈顶位置的运算符的优先级哪个高
//如果ch的优先级高则直接将我们的ch进行一个入栈
if(operStack.priority(ch) > operStack.priority(operStack.peek())){
operStack.push(ch);
}else{
//这个时候就是我们的ch的优先级比符号栈中栈顶位置的符号的优先级要高
//那么这个时候就要使用符号栈中栈顶位置的符号对我们的数栈中的栈顶位置和次栈顶位置的数据进行一个对应的运算
num1 = numStack.pop(); //此时就是我们的符号右边的值
num2 = numStack.pop(); //此时就是我们的符号左边的值
oper = operStack.pop(); //记录我们的符号栈中出栈的栈顶数据
//进行一个运算
res = operStack.col(num1,num2,oper);
//运算完成之后要让我们的运算结果入数栈,让我们的ch入符号栈
operStack.push(ch);
numStack.push(res);
}
}
}else{
//如果是数就直接入数栈
numStack.push(ch - 48); //将对应的字符1转换为对应的数值1 , 字符1转换成数值1之后的值为49
}
//让index++并且判断index是否是指向了最后
index++;
if (index >= expression.length()){
break;
}
}
//表达式扫描完毕之后,按照顺序从数栈和符号栈中取出数据进行对应的运算
while(true){
//如果符号栈为空的时候对应的数栈中就只剩下了一个值,这个值就是我们的最终运算结果
if (operStack.isEmpty()){
break;
}
num1 = numStack.pop(); //此时就是我们的符号右边的值
num2 = numStack.pop(); //此时就是我们的符号左边的值
oper = operStack.pop(); //记录我们的符号栈中出栈的栈顶数据
res = operStack.col(num1,num2,oper);
//每次运算之后都要将我们运算的结果入数栈
numStack.push(res);
}
//将数栈中的最后一个值输出(也就是对结果进行一个输出) ---> 我们数栈中剩余的最后一个元素就是我们的运算结果
int res2 = numStack.pop();
System.out.printf("表达式%s = %d",expression,res2);
}
}
补充:
这个时候其实我们就是实现了中缀表达式的一个计算机计算,我们可以发现: 对于中缀表达式的计算中我们使用到了两个栈
- 后面我们会将后缀表达式的计算 —> 在后缀表达式的计算中我们只需要使用到一个栈
- 然后再往后面我们就会学习中缀表达式转为后缀表达式 —> 在中缀表达式转后缀表达式的时候我们也是要使用到两个栈
- 或者是一个栈和一个队列( 又或者是一个栈和一个集合都是可以的)