使用数组模拟栈,实现综合计算器功能
定义一个栈
栈中的属性分别为栈的大小,栈数组,指向栈顶的top,无数据是指向-1;
private int maxSize;//栈的大小
private int[] stack;//数组,数组模拟栈,数据放在该数组中
private int top = -1;//top表示栈顶,初始化为-1
定义一个构造器,传进来栈的大小数值maxSize,构建一个maxSize大小的数组。
//构造器,用于初始化栈
public ArrayStack2(int maxSize) {
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
定义栈满的方法,当栈满的时候top == maxSize - 1;因为数组下标从零开始的。
//栈满
public boolean isFull() {
//-1 的目的是因为 数组下标是从0开始的,所以下标最大到maxSize-1
return top == maxSize - 1;
}
定义栈空的方法,栈空的时候top == -1;
//栈空
public boolean isEmpty() {
return top == -1;
}
定义查看栈顶元素的方法,首先判断栈是否为空,不为空返回top所指向的数据即可。
//查看栈顶元素的方法
public int lookTop() {
if (isEmpty()) {
System.out.println("栈中没有数据~");
}
return stack[top];
}
定义入栈操作方法,如果栈没有满,进行入栈操作。
//入栈
public void push(int value) {
//先判断栈满了没
if (isFull()) {
System.out.println("栈满~");
return;
}
//进行入栈操作
top++;
stack[top] = value;
}
定义出栈操作方法,如果栈不为空的时候进行出栈,出栈就是返回当前top所指向的数据,然后将top–即可。
//出栈
public int pop() {
//判断栈是否为空
if (isEmpty()) {
System.out.println("栈为空~");
throw new RuntimeException("栈为空~");
}
//进行出栈操作
int result = stack[top];
// System.out.printf("出栈数据为:%d\t\n", stack[top]);
top--;
return result;
}
遍历栈中数据方法,如果栈不为空,就从top开始遍历一次i–一次。
//遍历栈:从栈顶显示数据
public void list() {
//判断
if (isEmpty()) {
System.out.println("栈空~");
return;
}
//遍历操作
for (int i = top; i >= 0; i--) {
System.out.printf("遍历数据为:%d\t\n", stack[i]);
}
}
现在传过来一个表达式"3+2*6-2",我们应该怎么去计算呢?使用栈可以实现表达式得计算,思路如下:
1、通过一个index值(索引),来遍历我们的表达式。
2、如果我们发现是一个数字,就直接入栈。
3、如果我们发现扫描到的是一个符号,就需要分以下情况。
3.1、如果发现当前的符号栈为空,就直接入栈。
3.2、如果符号栈有操作符,就进行比较,如果当前的操作符优先级小于或者等于栈顶的操作符,就需要从数栈中取出两个数,从符号栈取出栈顶符号,进行运算,将运算结果放入数栈,然后将当前操作符放入符号栈,如果当前的操作符的优先级大于栈顶的操作符,就直接放入操作符。
4、当前表达式扫描完毕,就顺序的从数栈和符号栈中弹出相应的数和符号进行运算。
5、最后在数栈中只有一个数字,就是表达式的结果。
想要实现上面的功能,我们需要扩充栈的功能。
判断是不是一个运算符的方法,如果是+、-、*、/、就返回true:
//判断是不是一个运算符
public boolean isOper(char val) {
return val == '+' || val == '-' || val == '*' || val == '/';
}
返回运算符的优先级,优先级是程序员来确定的,优先级使用数字表示,数字越大,则优先级越高:
//返回运算符的优先级,优先级是程序员来确定的,优先级使用数字表示,数字越大,则优先级越高
public int priority(int oper) {
if (oper == '*' || oper == '/') {
return 1;
} else if (oper == '+' || oper == '-') {
return 0;
} else {
return -1;//假定目前表达式只有+、-、/、*
}
}
实现计算功能的方法,需要传过来两个数据和一个运算符:
//计算功能
public int cal(int num1, int num2, int oper) {
int res = 0;//res 用于存放计算的结果
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;
}
接下来,我们就需要在主方法中实现计算器的逻辑了。
代码详解:
1、我们首先定义这个算式,并将其转化成一个数组,方便后面遍历。
2、然后我们定义两个栈,一个数栈,一个符号栈,给定最大值。
3、定义相关变量,后面需要用。
4、开始对表达式数组进行循环遍历。
5、首先进行判断是数字还是运算符,如果是运算符,判断运算符栈是否为空,为空直接放入,不为空则进行下面的操作:
5.1、判断当前符号栈是否为空,如果符号栈有操作符,就进行比较,如果操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,在从符号栈中pop出一个符号,进行运算,得到结果,放入数栈,然后将当前的操作符放入符号栈,如果大于栈中的运算符,则直接放入运算符栈中。
5.2、如果是数字的时候需要往这个数字的后面一位在继续进行是否为运算符判断,因为如果为多位数123,需要放入123,而不是1、2、3,将遍历到的非运算符的字符进行拼接,然后转换成数字,存入数栈中,放入之后们一定需要将拼接字符串keepNum =”“,否则下次会接着这个来的,当后面的一位为运算符的时候就需要结束的,但是需要注意的是,我们最外层的遍历表达式数组的循环,在当每往后面看一位是不是数字的时候,如果是数字,我们都得让i++一下,否则假如是123,他把123存入之后,醉眠的循环又从23开始了。
6、此时表达式已经全部入栈了,我们需要把数栈和字符栈的的数据进行运算,依次从数栈取出两个数据,从符号栈取出一个符号,进行计算,将结果再放回数栈,直到符号栈为空,这时候数栈只有一个最后结果,我们输出便计算完毕。
代码看着不是很难,当你自己写的时候就会发现写不出来,需要考虑的东西很多,希望大家能自己写一遍,此时代码有一点问题,就是计算的结果只能是整数,因为我定义栈为int类型,大家可以完善一下,代码如下:
public static void main(String[] args) {
// String expression = "3+2*6-2";
String expression = "3-5+9*100/2-50";
char[] chars = expression.toCharArray();
//创建两个栈,一个存放数据,一个存放运算符
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
//定义需要的相关变量
int num1 = 0;
int num2 = 0;
int oper = 0;
int res = 0;
String keepNum = "";
//开始循环
for (int i = 0; i < chars.length; i++) {
//判断是什么
if (operStack.isOper(chars[i])) {
//如果是运算符,判断当前运算符栈是否为空
if (operStack.isEmpty()) {
operStack.push(chars[i]);
} else {
//如果符号栈有操作符,就进行比较,如果操作符的优先级小于或者等于栈中的操作符,就需要从数栈中pop出两个数,
//在从符号栈中pop出一个符号,进行运算,得到结果,放入数栈,然后将当前的操作符放入符号栈
if (operStack.priority(chars[i]) <= operStack.priority(operStack.lookTop())) {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
int result = numStack.cal(num1, num2, oper);
numStack.push(result);
operStack.push(chars[i]);
} else {
operStack.push(chars[i]);
}
}
} else {
keepNum=keepNum+chars[i];
//往后面看,看看是否还是数字,是的话拼接,然后得让i++
//原因:假如数字是123,i到3的后面一位为运算符,结束循环后,外面的循环从2又开始了,所以每找到非运算符就要拼接和i++
while (i < chars.length - 1 && !operStack.isOper(chars[i + 1])) {
keepNum = keepNum + chars[i + 1];
i++;
}
numStack.push(Integer.parseInt(keepNum));
//一定要归零,如果有2个以上多位数,就乱了。
keepNum="";
}
}
//此时表达式的数据和运算符已全部入栈,我们只需要计算出最后结果即可
while (true) {
if (!operStack.isEmpty()) {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
int result = numStack.cal(num1, num2, oper);
numStack.push(result);
} else {
break;
}
}
System.out.println(expression + "=" + numStack.lookTop());
}
运算结果如下:
3+2*6-2=13