// 枚举, 对应加减乘除
public enum OperatorEum {
ADD("+"),
SUBSTRACT("-"),
MULTIPY("*"),
DIVIDE("/");
private String operatorSign;
private OperatorEum(String operatorSign) {
this.operatorSign = operatorSign;
}
public String getOperatorSign(){
return operatorSign;
}
public static OperatorEum findOperatorEnum(String operatorSign) {
for(OperatorEum value : values()) {
if(value.getOperatorSign().equals(operatorSign)) {
return value;
}
}
return null;
}
}
import java.util.Arrays;
// 定义一个栈,用来保存用于计算的数字和加减乘除符号
// 主要提供pop, peek, empty, size方法
public class CustomStack<T> {
private int capacity;
private int size = 0;
private static int MAX = 16;
private Object[] dataArr;
public CustomStack() {
this.capacity = MAX;
dataArr = new Object[this.capacity];
}
public CustomStack(int capacity) {
this.capacity = capacity;
dataArr = new Object[this.capacity];
}
public boolean push(T data) {
if(dataArr.length == size){
return false;
}
dataArr[size++] = data;
return true;
}
public T pop() {
T data = (T)dataArr[--size];
dataArr[size] = null;
return data;
}
public T peek(){
return (T)dataArr[size-1];
}
public boolean empty(){
return size == 0;
}
public int size() { return size;}
@Override
public String toString() {
return "CustomStack{" +
"dataArr=" + Arrays.toString(dataArr) +
'}';
}
public static void main(String[] args) {
}
}
package test.stack;
import java.util.ArrayList;
import java.util.List;
public class CustomMath {
// 保存在计算过程要用于计算的数字和临时计算结果
private CustomStack<Integer> numberStack;
// 保存在计算过程中要用于计算的运算符号
private CustomStack<OperatorEum> operatorStack;
// 按顺序保存数学式子中所有的数字
private List<Integer> numberList;
// 按顺序保存数学式子中所有的运算符号
private List<OperatorEum> operatorEumList;
public CustomMath(){
numberStack = new CustomStack<>(10);
operatorStack = new CustomStack<>(10);
numberList = new ArrayList<>();
operatorEumList = new ArrayList<>();
}
// 正式进行计算
public Integer calculate(String mathExpression) throws Exception {
System.out.println("mathExpression: "+mathExpression);
Integer result = 0;
if(checkExpression()){
extractNumberAndOperatorToList(mathExpression);
int numberSize = numberList.size();
numberStack.push(numberList.get(0));
for(int i=1; i<numberSize; i++){
Integer number = numberList.get(i);
int j=i-1;
OperatorEum operatorEum = operatorEumList.get(j);
if(operatorStack.size() == 0){
numberStack.push(number);
operatorStack.push(operatorEum);
}else{
OperatorEum storedOperatorEum = operatorStack.peek();
if(storedOperatorEum.equals(operatorEum)){
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
result = calculate(number2, number1, operatorStack.pop());
numberStack.push(result);
numberStack.push(number);
operatorStack.push(operatorEum);
}else {
boolean superior = isSuperiorToStackOperator(storedOperatorEum, operatorEum);
if (superior) {
result = calculate(numberStack.pop(), number, operatorEum);
numberStack.push(result);
} else {
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
result = calculate(number2, number1, operatorStack.pop());
numberStack.push(result);
numberStack.push(number);
operatorStack.push(operatorEum);
}
}
}
}
}else{
throw new Exception("Illegal math expression");
}
while (numberStack.size() > 1){
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
result = calculate(number2,number1, operatorStack.pop());
numberStack.push(result);
}
return numberStack.pop();
}
// 具体的某种计算, 思路 number1 运算符号 number2, 两个参数的位置很重要,决定了
// 在计算式了中的位置,直接影响计算结果,比如3-2与2-3,
private Integer calculate(Integer number1, Integer number2, OperatorEum operatorEum){
Integer result = 0;
switch (operatorEum) {
case ADD:
result = number1+number2;
break;
case SUBSTRACT:
result = number1-number2;
break;
case MULTIPY:
result = number1*number2;
break;
case DIVIDE:
result = number1/number2;
break;
}
return result;
}
// 将数学式子中的数字和运算符号按顺序保存到对应的List中
private void extractNumberAndOperatorToList(String mathExpression){
String numberStr = "";
int length = mathExpression.length();
for(int i=0; i<length; i++){
char ch = mathExpression.charAt(i);
OperatorEum operatorEum = OperatorEum.findOperatorEnum(String.valueOf(ch));
if(operatorEum == null) {
numberStr += ch;
}else{
this.numberList.add(Integer.valueOf(numberStr));
numberStr = "";
this.operatorEumList.add(operatorEum);
}
}
this.numberList.add(Integer.valueOf(numberStr));
}
// 一般性的校验,检查一下输入的数学式子是否合未能
private boolean checkExpression(){
return true;
}
// 比较后一个运算符号的级别是否高于前一个运算符号,参数的位置很重要,
// 前一个运算符号要作为第一个参数, 后一个运算符号要作为第二个参数
private boolean isSuperiorToStackOperator(OperatorEum operatorEum1, OperatorEum operatorEum2){
String stackOperator = operatorEum1.getOperatorSign();
String operator = operatorEum2.getOperatorSign();
boolean highPriority = operator.equals("*") || operator.equals("/");
boolean lowPriority = stackOperator.equals("+") || stackOperator.equals("-");
return highPriority && lowPriority;
}
// 进行测试
public static void main(String[] args) {
String expression = "0-1/1+2*2/2-1"; //
CustomMath customMath = new CustomMath();
try {
Integer result = customMath.calculate(expression);
System.out.println(result);
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}
核心思路讲解:
1. numberStack中只保存两个数字, operatorStack中只保存一个运算符号,这样两个栈中数据合起来就是一个完整的数学表达式: a+b,两个栈中的数据一直维持这种状态。 2.初次入栈。在第一次执行numberList的遍历前,先在numberStack中放入一个数字numberStack.push(numberList.get(0)),然后从1开始遍历。经过下面步骤,两个栈中的数据满足了第1点要求。
if(operatorStack.size() == 0){
numberStack.push(number);
operatorStack.push(operatorEum);
}
3. 遍历计算及入栈讲解,看代码按思路分析。
// storedOperatorEum为符号栈中保存的运算符号, operatorEum为从符号list取出的运算符号
// number是从数字list中取出的数字,将用来计算或是入栈
OperatorEum storedOperatorEum = operatorStack.peek();
if(storedOperatorEum.equals(operatorEum)){
//如果运算符号相同,先将栈中数字和运算符号弹出并进行计算,将计算结果入栈保存,再将number入栈保存,
// 将operatorEum入栈保存
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
// 此处要注意,一定是第二次弹出的数字作为第一个运算数字,第一次弹出的数字作为第二个运算数字
// 因为3-2与2-3的结果就完全不同
result = calculate(number2, number1, operatorStack.pop());
numberStack.push(result);
numberStack.push(number);
operatorStack.push(operatorEum);
}else {
boolean superior = isSuperiorToStackOperator(storedOperatorEum, operatorEum);
if (superior) {
// operatorEum的级别高于栈中运算符号的级别,就要数字栈中顶元素与number进行运算
// 将计算结果入栈。 要注意的是数字栈中的顶元素要作为第一个运算数字, number是作为第二个
// 此时,operatorEum与number都完成了计算,所以不需要入栈了
result = calculate(numberStack.pop(), number, operatorEum);
numberStack.push(result);
} else {
// 如果operatorEum的级别与栈中运算符号的级别相等,则将栈中数字和运算符号弹出并进行计算,将计算// 结果入栈保存。
// 然后number与operatorEum要进行入栈
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
result = calculate(number2, number1, operatorStack.pop());
numberStack.push(result);
numberStack.push(number);
operatorStack.push(operatorEum);
}
}