用栈来实现计算器这个是个基础的常见的算法题 ,如何利用栈来实现计算机呢? 核心思想就是加入传来一个字符串expression(表达式)利用subString来进行一个字符一个字符的扫描,这样做就会出现两种情况一种是数字一种是操作符假设第一种情况扫描到数字的时候直接把他压入数字栈,然而碰到的是操作符的时候还得分两种情况来讨论: 第一种情况:操作符栈为空,直接压栈就好了 第二种情况:操作符栈不为空这时得进行判断操作符栈的栈顶操作符的优先级是否比要入栈的大,如果是的话就得做以下操作; 先从数字栈取出两个数字 num1 num2 然后在从操作符栈取出栈顶操作符他们进行运算后的结果重新压入数字栈,在做完结果压入数字栈后再把当前的操作符入栈就OK了; 单单来看,原理很简单所以我直接贴上代码,代码中注释写的也很详细
public class CalculatorDemo {
public static void main(String[] args) {
//表达式
String expression = "7-(1-8)*2";
//数字栈
ArrayStack4 numStack = new ArrayStack4(10);
//符号栈
ArrayStack4 operStack = new ArrayStack4(10);
int index = 0;//用于扫描
int num1 = 0;
int num2 = 0;
int result = 0;
int oper = 0;
char ch = ' ';//把扫描到的符号存在ch中
String keepNum = "";//用于拼接的字符串
//开始扫描表达式什么情况开始推出扫描就是当index>=expression.length()的时候跳出
while (true) {
//从index指针开始取值,取一个 charAt(0)代表取他的第一个值
//注意的是int类型的 ‘1’比如这个的Assi码是49要取到真实的数字必须-48
ch = expression.substring(index, index + 1).charAt(0);
//判断是否是字符
//是字符的情况还得考虑两种情况是否为空?不为空的时候在栈顶的操作符优先级是否大于现在的优先级
if (ArrayStackUtil1.isOper(ch)) {
//当扫描到了(括号的时候
if (ch == '(') {
index++;//这个++为了跳过(左括号
ArrayStack4 subNumStack = new ArrayStack4(10);
ArrayStack4 subOperStack = new ArrayStack4(10);
int subNum1 = 0;
int subNum2 = 0;
int subResult = 0;
char subCh = ' ';
int subOper = 0;
while (true) {
subCh = expression.substring(index, index + 1).charAt(0);
if (ArrayStackUtil1.isOper(subCh)) {
int j=0;
if (subCh == 41) {
break;
}
if (!subOperStack.isEmpty()) {
if (ArrayStackUtil1.priorityOper(subCh) <= ArrayStackUtil1.priorityOper(subOperStack.peep())) {
subNum1 = subNumStack.pop();
subNum2 = subNumStack.pop();
subOper = subOperStack.pop();
subResult = ArrayStackUtil1.cal(subNum1, subNum2, subOper);
subNumStack.push(subResult);
subOperStack.push(subCh);
} else {
subOperStack.push(subCh);
}
} else {
subOperStack.push(subCh);
}
} else {
keepNum += subCh;
if (index == expression.length() - 1) {
subNumStack.push(Integer.parseInt(keepNum));
keepNum = "";
} else {
if (ArrayStackUtil1.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
subNumStack.push(Integer.parseInt(keepNum));
keepNum = "";
}
}
}
index++;
}
while (true) {
if (subOperStack.isEmpty()) {
break;
}
subNum1 = subNumStack.pop();
subNum2 = subNumStack.pop();
subOper = subOperStack.pop();
result = ArrayStackUtil1.cal(subNum1, subNum2, subOper);
subNumStack.push(result);
}
numStack.push(subNumStack.pop());
} else {
//判断操作符栈是否为空
// 不为空的情况
if (!operStack.isEmpty()) {
//不为空的情况下开始比较优先级
// 当当前的ch小于栈顶的优先级的时候去栈顶的表达式运算符出来 和数字栈的两个数字出来开始进行运算
if (ArrayStackUtil1.priorityOper(ch) <= ArrayStackUtil1.priorityOper(operStack.peep())) {
oper = operStack.pop();
num1 = numStack.pop();
num2 = numStack.pop();
result = ArrayStackUtil.cal(num1, num2, oper);
//将计算出来的结果重新压入数字栈,ch也压入符号栈
numStack.push(result);
operStack.push(ch);
} else {
//假如当前ch操作符优先级大于栈顶的直接入栈
operStack.push(ch);
}
} else {
//为空的情况下直接入栈
operStack.push(ch);
}
}
}
//不是字符的情况先判断他下一位是否是字符
else {
//为什么可以直接加上字符类型而不用考虑减去48呢
//因为当char类型和int类型比较式才会转换成ASCII这时候才需要减去48
keepNum += ch;
if (index == expression.length() - 1) {
numStack.push(Integer.parseInt(keepNum));
} else {
if (ArrayStackUtil1.isOper(expression.substring(index + 1, index + 2).charAt(0))) {
numStack.push(Integer.parseInt(keepNum));
keepNum = "";
}
}
}
//扫描指针向后移
index++;
//当指针大于或者等于表达式长度的时候退出扫描
if (index >= expression.length()) {
break;
}
}
//开始计算
while (true) {
//结束计算的结果是operStack栈为空的时候
if (operStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
//将计算出来的结果在重新压入数字栈中
result = ArrayStackUtil1.cal(num1, num2, oper);
numStack.push(result);
}
System.out.println("表达式" + expression + "的结果是:" + numStack.pop());
}
}
class ArrayStack4 {
private int maxSize;
private int[] stack;
private int top = -1;
public ArrayStack4(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 value) {
if (isFull()) {
System.out.println("栈满");
return;
} else {
top++;
stack[top] = value;
}
}
public int pop() {
if (isEmpty()) {
throw new RuntimeException("栈空");
} else {
int value = stack[top];
top--;
return value;
}
}
public void list() {
if (isEmpty()) {
System.out.println("栈空");
return;
}
for (int i = top; i >= 0; i--) {
System.out.println(i);
System.out.println(stack[i]);
}
}
public int peep() {
return stack[top];
}
}
class ArrayStackUtil1 {
public static boolean isOper(int val) {
return val == '+' || val == '-' || val == '/' || val == '*' || val == '(' || val == ')';
}
public static int priorityOper(int oper) {
if (oper == '+' || oper == '-') {
return 0;
} else if (oper == '*' || oper == '/') {
return 1;
} else {
return -1;
}
}
public static int cal(int num1, int num2, int oper) {
int result = 0;
switch (oper) {
case '+':
result = num1 + num2;
break;
case '-':
result = num2 - num1;
break;
case '*':
result = num1 * num2;
break;
case '/':
result = num2 / num1;
break;
}
return result;
}
}
通过这上面的代码实现的计算机可能有人会问 ?假如表达式比如1-2+3这个时候先扫描到1在扫描到2然后下一次扫描到+号,按照我们的数学语言应该从左到右运算,这个代码如何保证从左到右运算呢?
if (ArrayStackUtil1.priorityOper(subCh) <= ArrayStackUtil1.priorityOper(subOperStack.peep())
我们可以看到这个if语句中不是判断他的优先级小于的情况而是小于等于这么来说就以上面的表达式1-2+3当,扫描到+号的时候发现符号栈里面已经有了-且他们的优先级是相等所以就先把1和2取出来做减法运算在把结果压入数字栈中这时候是不是和我们数学语言从左到右运算是一样的。