阿里笔试题 - 算数表达式

设计数据结构与算法,计算算数表达式,需要支持:

  1. 基本计算,加减乘除,满足计算优先级 例如输入 30+3+8+91 输出20
  2. 括号,支持括号,例如输入 3+(3-0)*2 输出 9
  3. 假设所有的数字均为整数,无需考虑精度问题

要求:

  1. 输入的表达式是字符串类型String。
  2. 对于操作数要求不止一位,这里对字符串里面解析出操作数有要求。需要有从表达式里面解析出完整操作数的能力。
  3. 代码结构要求具备一定的面向对象原则,能够定义出表达式,操作数,运算符等对象。
  4. 提供基本的测试。
/**
 * 运算引擎
 */
public class CalculateEngine {
    /**
     * 操作符栈
     */
    private Stack<String> operatorStack;

    /**
     * 操作数栈
     */
    private Stack<Integer> operateNumberStack;

    /**
     * 后缀表达式输出
     */
    private StringBuilder output;

    public CalculateEngine() {
        operatorStack = new Stack<>();
        operateNumberStack = new Stack<>();
        output = new StringBuilder();

    }

    /**
     * 表达式计算
     * @param infixExpression
     * @return
     */
    public Integer eval(String infixExpression) {
        String postfixExpression = toPostfixExpression(infixExpression);
        return calculatePostfixExpression(postfixExpression);
    }

    /**
     * 中缀表达式转后缀表达式
     * @param infixExpression
     * @return
     */
    private String toPostfixExpression(String infixExpression) {
        infixExpression = formatInfixExpression(infixExpression);
        //System.out.println("Formatted Infix Expression: " + infixExpression);
        String[] infixExpressionArray = infixExpression.split(" ");//split("\\+|-|\\*|/|\\(|\\)");
        Operator operator = null, topOperator = null;
        String topSymbol = null;
        for (String item : infixExpressionArray) {
            if (item == null || item.trim().equals("")) {
                continue;
            }
            // item为操作符,比较与栈顶操作符的优先级
            if ((operator = Operator.from(item)) != null) {
                // 操作符栈为空直接压栈
                if (operatorStack.isEmpty()) {
                    operatorStack.push(item);
                } else {
                    // item为右括号,将操作符栈输出,直到输出为左括号为止
                    if (operator == Operator.RIGHT_BRACKETS) {
                        topSymbol = operatorStack.pop();
                        topOperator = Operator.from(topSymbol);
                        while (topOperator != Operator.LEFT_BRACKETS) {
                            appendOutput(topSymbol);
                            topSymbol = operatorStack.pop();
                            topOperator = Operator.from(topSymbol);
                        }
                    } else {
                        topSymbol = operatorStack.peek();
                        topOperator = Operator.from(topSymbol);
                        // 栈顶操作不为左括号 && 栈顶操作符优先级 >= 当前操作符优先级,出栈输出到后缀表达式
                        if (operator != Operator.LEFT_BRACKETS && topOperator.priority() >= operator.priority() && topSymbol.equals(operatorStack.pop())) {
                            appendOutput(topSymbol);
                        }
                        operatorStack.push(item);
                    }
                }
            } else {
                // 数字直接输出
                appendOutput(item);
            }
        }
        // 输出操作符栈的剩余操作符到后缀表达式中
        while (!operatorStack.isEmpty()) {
            topSymbol = operatorStack.pop();
            appendOutput(topSymbol);
        }
        //System.out.println("Postfix Expression: " + output.toString());
        return output.toString();
    }

    /**
     * 格式化中缀表达式
     * @param infixExpression
     * @return
     */
    private String formatInfixExpression(String infixExpression) {
        infixExpression = infixExpression.replace(" ", "");
        infixExpression = infixExpression.replace("+", " + ");
        infixExpression = infixExpression.replace("-", " - ");
        infixExpression = infixExpression.replace("*", " * ");
        infixExpression = infixExpression.replace("/", " / ");
        infixExpression = infixExpression.replace("+", " + ");
        infixExpression = infixExpression.replace("(", "( ");
        infixExpression = infixExpression.replace(")", " )");
        infixExpression = infixExpression.replace("(", "( ");
        infixExpression = infixExpression.replace(")", " )");
        return infixExpression;
    }

    /**
     * 后缀表达式附加
     * @param item
     */
    private void appendOutput(String item) {
        if (output.length() > 0) {
            output.append(" ");
        }
        output.append(item);
    }

    /**
     * 后缀表达式结果计算
     * @param postfixExpression
     * @return
     */
    private Integer calculatePostfixExpression(String postfixExpression) {
        String[] postfixExpressionArray = postfixExpression.split(" ");
        Operator operator;
        Integer result = null;
        String item;
        for (int i = 0; i < postfixExpressionArray.length; i++) {
            item = postfixExpressionArray[i];
            // 如果item为操作符则从操作数栈去出栈2个操作数计算结果,将结果压栈
            if ((operator = Operator.from(item)) != null) {
                Integer secondOperateNumber = operateNumberStack.pop();
                Integer firstOperateNumber = operateNumberStack.pop();
                Operation operation = OperationFactory.get(operator);
                result = operation.getResult(firstOperateNumber, secondOperateNumber);
                if (i < postfixExpressionArray.length - 1) {
                    operateNumberStack.push(result);
                }
            }
            // 如果item为操作数直接压栈
            else {
                operateNumberStack.push(Integer.valueOf(item));
            }
        }
        return result;
    }
}
/**
 * 操作符
 */
public enum Operator {
    PLUS("+", 1),
    SUBTRACT("-", 1),
    MULTIPLY("*", 2),
    DIVIDE("/", 2),
    LEFT_BRACKETS("(", 0),
    RIGHT_BRACKETS(")", 3);

    private String symbol;
    private int priority;
    Operator(String symbol, int priority) {
        this.symbol = symbol;
        this.priority = priority;
    }

    public String symbol() {
        return symbol;
    }

    public int priority() {
        return priority;
    }

    public Operator of(String symbol) {
        for (Operator operator : values()) {
            if (operator.symbol.equals(symbol)) {
                return operator;
            }
        }
        throw new IllegalArgumentException("invalid symbol: " + symbol);
    }

    public static Operator from(String symbol) {
        for (Operator operator : values()) {
            if (operator.symbol.equals(symbol)) {
                return operator;
            }
        }
        return null;
    }
}
import com.ant.interview.expression.operation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * 运算工厂
 */
public class OperationFactory {
    private static final Map<Operator, Operation> operationMap = new HashMap<Operator, Operation>(4) {
        {
            put(Operator.PLUS, new PlusOperation());
            put(Operator.SUBTRACT, new SubtractOperation());
            put(Operator.MULTIPLY, new MultiplyOperation());
            put(Operator.DIVIDE, new DivideOperation());
        }
    };

    public static Operation get(Operator operator) {
        return operationMap.get(operator);
    }
}
public interface Operation {
    Integer getResult(Integer firstOperateNumber, Integer secondOperateNumber);
}
/**
 * 加法运算
 */
public class PlusOperation implements Operation {
    @Override
    public Integer getResult(Integer firstOperateNumber, Integer secondOperateNumber) {
        Integer result = firstOperateNumber.intValue() + secondOperateNumber.intValue();
        //System.out.println(String.format("%d + %d = %d", firstOperateNumber.intValue(), secondOperateNumber.intValue(), result.intValue()));
        return result;
    }
}
/**
 * 减法运算
 */
public class SubtractOperation implements Operation {
    @Override
    public Integer getResult(Integer firstOperateNumber, Integer secondOperateNumber) {
        Integer result = firstOperateNumber.intValue() - secondOperateNumber.intValue();
        //System.out.println(String.format("%d - %d = %d", firstOperateNumber.intValue(), secondOperateNumber.intValue(), result.intValue()));
        return result;
    }
}
/**
 * 乘法运算
 */
public class MultiplyOperation implements Operation {
    @Override
    public Integer getResult(Integer firstOperateNumber, Integer secondOperateNumber) {
        Integer result = firstOperateNumber.intValue() * secondOperateNumber.intValue();
        //System.out.println(String.format("%d * %d = %d", firstOperateNumber.intValue(), secondOperateNumber.intValue(), result.intValue()));
        return result;
    }
}
/**
 * 除法运算
 */
public class DivideOperation implements Operation {
    @Override
    public Integer getResult(Integer firstOperateNumber, Integer secondOperateNumber) {
        Integer result = firstOperateNumber.intValue() / secondOperateNumber.intValue();
        //System.out.println(String.format("%d / %d = %d", firstOperateNumber.intValue(), secondOperateNumber.intValue(), result.intValue()));
        return result;
    }
}
import com.ant.interview.expression.CalculateEngine;
import org.junit.Test;

public class ExpressionTest {

    @Test
    public void testExpression() {
        String[] expressionArray = new String[] {
                "3*0+3+8+9*1",
                "3+(3-0)*2"
        };

        CalculateEngine calculateEngine = new CalculateEngine();
        for (String expression : expressionArray) {
            int result = calculateEngine.eval(expression);
            System.out.println(String.format("%s = %d", expression, result));
        }
    }
}

思考:

  1. 为什么要把中缀表达式转化为后缀,前缀?
    计算机没法计算带有括号,以及区分优先级的表达式,或者说很难计算。使用后缀,前缀,消除了括号和优先级。
  2. 中缀表达式如何转化为后缀,前缀表达式?
    转化为后缀:从左到右遍历中缀表达式,遇到操作数,输出,遇到操作符,当前操作符的优先级大于栈顶操作符优先级,进栈,否则,弹出栈顶优先级大于等于当前操作符的操作符,当前操作符进栈。
    转化为前缀:从右到左遍历中缀表达式,遇到操作数,输出,遇到操作符,当前操作符的优先级大于等于栈顶操作符优先级,进栈,否则,弹出栈顶优先级大于当前操作符的操作符,当前操作符进栈。
  3. 计算机如何计算后缀,前缀表达式?
    计算后缀:从左到右遍历后缀表达式,遇到操作数,放进栈,遇到操作符,栈顶两个数出栈,进行运算,运算结果放进栈,直到读完后缀表达式。
    计算前缀:从左到右遍历前缀表达式,遇到操作符,放进栈,遇到操作数,查看栈顶,栈顶为操作符,放进栈,栈顶为操作数,取出栈顶操作数和操作符,进行运算,运算后继续判断栈顶的情况。

参考:
https://www.cnblogs.com/hantalk/p/8734511.html
https://blog.csdn.net/baidu_31497293/article/details/53164395

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值