计算器【Java算法(六)】
写在前面的话:
- 本次参考资料:如何用C语言编写一个计算器(川川菜鸟)
- 所需知识点:了解栈的基本原理,出栈与入栈
- IDE:IntelliJ IDEA 2021.2.1
- JDK:Java8
目录
项目结构:
本次代码if-else语句较多,请耐心观看。
遍历整个计算式时,笔者将其分为3个部分:
- 遍历的位置含有减号(-)
- 遍历的位置含有左括号(()和减号(-)
- 遍历的位置是数字
- 遍历的位置是操作符
1.计算器类
Calculator.java
package com.application;
/*
栈的实际应用:计算器
*/
public class Calculator {
private String calculate;//输入的计算式子 ==> 字符串形式
private double[] operand;//放操作数的栈,double ==> 该计算器支持浮点数运算
private int operandTop;//操作数的栈顶
private char[] operator;//放操作符的栈
private int operatorTop;//操作符的栈顶
public Calculator(){
//初始化栈空间
operand = new double[10];
operator = new char[10];
}
public Calculator(String calculate){
//初始化栈空间
operand = new double[10];
operator = new char[10];
this.calculate = calculate;//传入一个计算式
}
//设置计算式,也可以理解为添加一个计算式
public void setCalculate(String calculate){
this.calculate = calculate;
}
public String getCalculate(){
return calculate;
}
public void setOperand(double[] operand){
this.operand = operand;
}
public void setOperator(char[] operator){
this.operator = operator;
}
/**
* 将操作数放入栈中
* @param element 插入的数据
*/
private void push(double element){
//判断栈满
if(operandTop >= operand.length){
expandCapacity(true);//操作数栈进行扩容
}
//放入元素
operand[operandTop] = element;
operandTop++;//操作数栈顶
}
//将操作符插入操作符栈中
private void push(char element){
//判断栈满
if(operatorTop >= operator.length){
expandCapacity(false);//操作符栈进行扩容
}
//插入操作符
operator[operatorTop] = element;
operatorTop++;//操作符栈顶加1
}
//操作数弹出
private double popNumber(){
return operand[--operandTop];
}
//操作符弹出
private char popOperator(){
return operator[--operatorTop];//只管弹出
}
/**
* 对数组进行扩容
* @param o true -> 操作数栈 false -> 操作符栈
*/
private void expandCapacity(boolean o){
//根据传入的参数决定array的数据类型
if(o){
double[] array = operand;
//扩容成原来的数组的两倍
double[] newArray = new double[array.length * 2];
//将原来数组中的元素全部复制到新数组当中
if (operandTop >= 0 || operatorTop >= 0){
System.arraycopy(array, 0, newArray, 0, array.length);
//将扩容后的数组的首地址赋值给array(该类的属性)
setOperand(newArray);
}
}else{
char[] array = operator;
char[] newArray = new char[array.length * 2];
//将原来数组中的元素全部复制到新数组当中
if (operandTop >= 0 || operatorTop >= 0){
System.arraycopy(array, 0, newArray, 0, array.length);
//将扩容后的数组的首地址赋值给array(该类的属性)
setOperator(newArray);
}
}
}
//返回结果
public double getResult(){
double result = 0.0;
double num1;
double num2;
StringBuilder value = new StringBuilder();//保存的中间值
if(calculate == null){
//计算式为空
return result;//结果为空,以0返回出去
}else{
//先检查计算式是否符合规范
if(calculate.charAt(0) == '*' || calculate.charAt(0) == '/' || calculate.charAt(0) == '+'){
throw new RuntimeException("计算式格式不准确!开头不允许出现+、*、/三种操作符,尾部只能是数字或右括号");
}
//检测尾部
if (calculate.charAt(calculate.length() - 1) < '0'|| calculate.charAt(calculate.length() - 1) > '9'){
//如果检测到计算式尾部是→括号,不会抛异常
if(calculate.charAt(calculate.length() - 1) != ')'){
//开头不允许出现+、*、/三种操作符,尾部只能是数字或右括号
throw new RuntimeException("计算式格式不准确!开头不允许出现+、*、/三种操作符,尾部只能是数字或右括号");
}
}
//判断计算式开头是数字?操作符?【遍历整个计算式】
for(int i = 0;i < calculate.length();i++){
//如果计算式以减号开头
if(calculate.charAt(i) == '-'){
value.append(calculate.charAt(i));
}else if(calculate.charAt(i) == '(' && calculate.charAt(i + 1) == '-'){
i++;
value.append(calculate.charAt(i));//将减号加入
i++;//判断下一个字符
//判断如果后面是数字,就加入value
while(calculate.charAt(i) >= '0' && calculate.charAt(i) <= '9'){
value.append(calculate.charAt(i));
i++;//依次判断下一个字符是否是数字,是数字添加进value,不是跳出循环
}
//将value值加入操作数栈中
if(value.charAt(0) == '-' && value.charAt(1) == '-'){
//连续的减号
String pushedValue;//放入栈中的值
pushedValue = value.substring(1);
push(Double.parseDouble(pushedValue));
push(value.charAt(0));//将减号压入符号栈中
}else{
push(Double.parseDouble(value.toString()));
}
value.delete(0,value.length());//清空
if(calculate.charAt(i) != ')'){
push('(');
i--;
}
}else if(calculate.charAt(i) >= '0' && calculate.charAt(i) <= '9'){//判断是否是数字
//判断如果后面是数字,就加入value
while(calculate.charAt(i) >= '0' && calculate.charAt(i) <= '9'){
if(i == calculate.length() - 1){
//计算式最后1个数字
value.append(calculate.charAt(i));
i++;
break;//跳出循环
}
value.append(calculate.charAt(i));
i++;//依次判断下一个字符是否是数字,是数字添加进value,不是跳出循环
}
push(Double.parseDouble(value.toString()));
value.delete(0,value.length());//清空
i--;
}else{//一些符号
if(operatorTop == 0){
//操作符栈没有符号
push(calculate.charAt(i));
}else if(priority(calculate.charAt(i)) == 1){//符号是(
push(calculate.charAt(i));
}else if(priority(calculate.charAt(i)) == 2){//符号是+或-
if(priority(operator[operatorTop - 1]) == 1) {//栈顶符号是(
push(calculate.charAt(i));
}else if(priority(operator[operatorTop - 1]) == 2 || //符号是+ -
priority(operator[operatorTop - 1]) == 3){ //符号是* /
//操作符个数大于1 以及操作数栈大于2,可以进行计算
while (operatorTop >= 1 && operandTop >= 2){
//判断一下当前操作符是+ - * /【在操作符栈中还有'('】
if(operator[operatorTop - 1] == '('){
break;//如果栈顶是左括号,直接跳出循环
}
num1 = popNumber();//弹出两个数
num2 = popNumber();
push(twoCalculate(num1,num2,popOperator()));//进行相加,结果存入操作数栈
}
push(calculate.charAt(i));//新操作符进栈
}
}else if(priority(calculate.charAt(i)) == 3){//符号是* /
if(priority(operator[operatorTop - 1]) == 1) {//栈顶符号是(
push(calculate.charAt(i));
}else if(priority(operator[operatorTop -1]) == 2){//符号是+ -
push(calculate.charAt(i));//新操作符进栈
}else if(priority(operator[operatorTop -1]) == 3){//符号是* /
//操作符个数大于1 以及操作数栈大于2,可以进行计算
while (operatorTop >= 1 && operandTop >= 2){
//判断一下当前操作符是+ - * /【在操作符栈中还有'('】
if(operator[operatorTop - 1] == '('){
break;//如果栈顶是左括号,直接跳出循环
}
num1 = popNumber();//弹出两个数
num2 = popNumber();
push(twoCalculate(num1,num2,popOperator()));//进行相加,结果存入操作数栈
}
push(calculate.charAt(i));//新操作符进栈
}
}else if(priority(calculate.charAt(i)) == 4) {//符号是)
do{//一直运算,直到遇到'('
//如果一直没有遇到'('
if(operatorTop <= 1){
throw new RuntimeException("计算式有误!()括号不对称");
}
num1 = popNumber();
num2 = popNumber();
push(twoCalculate(num1,num2,popOperator()));
}while (priority(operator[operatorTop - 1]) != 1);
popOperator();//操作符(出栈
}
}
}
}
//当操作符栈中还有操作符,一直计算
while (operatorTop != 0){
num1 = popNumber();
num2 = popNumber();
push(twoCalculate(num1,num2,popOperator()));
}
result = operand[operandTop - 1];
return result;
}
/**
* 优先级判断 【优先级由低到高:1 -- 4】
* @param c 运算符
*/
private int priority(char c){
if(c == '(' || c == '('){
return 1;
}else if(c == '+' || c == '-'){
return 2;
}else if(c == '*' || c == '/'){
return 3;
}else if(c == ')' || c == ')'){
return 4;
}else{
throw new IllegalArgumentException("参数错误!非运算符!");
}
}
/**
* 两个数进行计算
* @param num1 传入的数字1
* @param num2 传入的数字2
* @param c 传入的运算符
* @return 返回的结果
*/
private double twoCalculate(double num1,double num2,char c){
double result;
switch (c){
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if(num2 == 0){
throw new IllegalArgumentException("除数不能为0!");
}
result = num1 / num2;
break;
default:
throw new IllegalArgumentException("运算符不是 + - * /");
}
return result;//返回结果
}
}
2.测试计算器
TestCalculate.java
package com.application;
/*
测试计算器
*/
public class TestCalculate {
public static void main(String[] args) {
Calculator calculator = new Calculator("1+(2*3+1)+(2*32+12)+129+(12*89)");
double result = calculator.getResult();//获取结果
System.out.println(calculator.getCalculate() + "=" + result);
}
}
3.运行截图
完