实现RPN(逆波兰式)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28929579/article/details/90679677

什么是RPN逆波兰式?

逆波兰式(Reverse Polish notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后),举例:

Input: 2 1 + 3 *
Output: 9
Explanation: ((2 + 1) * 3) = 9

问题:设计一个栈来实现RPN功能,包含如下几点:

  1. 接受以空格区分的字符串,字符串包含数字和操作符。
  2. 操作符包含 + - * / sqrt undo clear
  3. 遇到操作符计算后把结果压入栈。
  4. 处理后输出显示结果。
  5. 栈存储15位精度的小数,但只显示10位小数。
  6. 当操作元素不够时,报错 比如 operator<*> (position:<10>):insufficient parameters
  7. 报错后终止后续已输入的内容并显示已处理的数据。
  8. sqrt是开平方,undo是撤销上一步内容,clear是清空栈元素。
    举例1
    Input: 5 2
    Output: stack: 5 2
    举例2
    Input: 2 sqrt
    Output: stack: stack:1.4142135623
    Input: clear 9 sqrt
    Output: stack:3
    Input: clear
    Output: stack:
    举例3
    Input: 5 4 3 2
    Output: stack: stack:5 4 3 2
    Input: undo undo *
    Output: stack: stack:20
    Input: 5 *
    Output: stack: stack:100
    举例4
    Input: 1 2 3 4 5
    Output: stack: stack:1 2 3 4 5
    Input: * * * *
    Output: stack: stack:120
    举例5
    Input: 1 2 3 * 5 + * * 6 5
    Output: stack: operator * (position:15):insufficient parameters stack:11

思考

题目要求都用栈了,这里就用LinkedList来实现栈,还需一个数组来记录以及输入的值,关键是undo操作,是从输入开始就重新计算还是只回撤上一步了?我考虑到设计sqrt、/等操作,回撤操作会丢失精度,那还是从头开始计算一遍吧。

实现

import java.math.BigDecimal;
import java.math.MathContext;
import java.util.*;

public class RPN {
    private LinkedList<BigDecimal> resultList = new LinkedList<>();
    private ArrayList<String> recordList = new ArrayList<>();

    //单个入栈
    private void push(BigDecimal val) {
        resultList.addFirst(val);
    }

    //查看栈顶元素但不移除
    private BigDecimal peek() {
        return resultList.getFirst();
    }

    //出栈
    private BigDecimal pop() {
        return resultList.removeFirst();
    }

    //判空
    private boolean empty() {
        return resultList.isEmpty();
    }

    //打印栈元素(显示10位精度)
    @Override
    public String toString() {
        StringBuilder str = new StringBuilder("stack:");
        System.out.print(str);
        for (int i = resultList.size() - 1; i >= 0; i--) {
            BigDecimal result = resultList.get(i);
            BigDecimal intValue = new BigDecimal(result.intValue());
            if (intValue.compareTo(result) != 0) {
                String showStr = result.setScale(10, BigDecimal.ROUND_DOWN).stripTrailingZeros().toPlainString();
                str.append(showStr + " ");
                System.out.print(showStr + " ");
            } else {
                str.append(intValue + " ");
                System.out.print(intValue + " ");
            }
        }
        System.out.println();
        return str.toString();
    }

    private boolean inputVal(String str) {
        String[] vals = str.split("\\s+");
        for (String val : vals) {
            recordList.add(val);
            if (isOperator(val)) {
                boolean flag = operator(val);
                if (!flag) {
                    toString();
                    return false;
                }
            } else {
                try {
                    BigDecimal num = new BigDecimal(val);
                    resultList.addFirst(num);
                } catch (NumberFormatException e) {
                    System.out.println("Please enter the correct number!");
                    recordList.remove(recordList.size() - 1);
                    break;
                }
            }
        }
        return true;
    }

    public void pushs(String str) {
        if (inputVal(str)) {
            toString();
        }
    }

    private void reset() {
        this.resultList = new LinkedList<>();
        this.recordList = new ArrayList<>();
    }

    private boolean operator(String opr) throws NoSuchElementException {
        int position = recordList.size();
        if (opr.equals("undo")) {
            // 重新计算
            if (position >= 3) {
                StringBuilder preStr = new StringBuilder();
                for (int i = 0; i <= position - 3; i++) {
                    preStr.append(recordList.get(i) + " ");
                }
                reset();
                inputVal(preStr.toString());
            }
        } else if (opr.equals("clear")) {
            reset();
        } else {
            BigDecimal topNum = null;
            try {
                topNum = pop();
                if (opr.equals("+")) {
                    BigDecimal laterNum = pop();
                    push(topNum.add(laterNum));
                } else if (opr.equals("-")) {
                    BigDecimal laterNum = pop();
                    push(laterNum.subtract(topNum));
                } else if (opr.equals("*")) {
                    BigDecimal laterNum = pop();
                    push(laterNum.multiply(topNum));
                } else if (opr.equals("/")) {
                    BigDecimal laterNum = pop();
                    push(laterNum.divide(topNum, 15, BigDecimal.ROUND_HALF_UP));
                } else if (opr.equals("sqrt")) {
                    push(sqrt(topNum).setScale(15, BigDecimal.ROUND_HALF_UP));
                }
            } catch (NoSuchElementException e) {
                System.out.print("operator " + opr + " (position:" + (position * 2 - 1) + "):insufficient parameters ");
                //把成功的计算结果塞回去
                push(topNum);
                // 截取记录符合的输入
                recordList.remove(--position);
                return false;
            }
        }
        return true;
    }

    private boolean isOperator(String opr) {
        return "+".equals(opr) || "-".equals(opr) || "*".equals(opr) || "sqrt".equals(opr) || "/".equals(opr) || "undo".equals(opr) || "clear".equals(opr);
    }

    private static BigDecimal sqrt(BigDecimal num) {
        if (num.compareTo(BigDecimal.ZERO) < 0) {
            return BigDecimal.ZERO;
        }
        BigDecimal x = num.divide(new BigDecimal("2"), MathContext.DECIMAL128);
        while (x.subtract(x = sqrtIteration(x, num)).abs().compareTo(new BigDecimal("0.0000000000000000000001")) > 0) ;
        return x;
    }

    private static BigDecimal sqrtIteration(BigDecimal x, BigDecimal n) {
        return x.add(n.divide(x, MathContext.DECIMAL128)).divide(new BigDecimal("2"), MathContext.DECIMAL128);
    }

    public static void main(String[] args) {
        RPN rpn = new RPN();
        Scanner sc = new Scanner(System.in);
        // 1 2 3 * * 2 /
        // 1 2 3 * 5 + * * 6 5
        while (true) {
            String input = sc.nextLine();
            rpn.pushs(input);
        }
    }
}

这是一道面试题,但是为什么没过就很尴尬,先记录下吧,或许等过些日子就能发现问题了。。。也欢迎各位指出不足

猜你喜欢

转载自blog.csdn.net/qq_28929579/article/details/90679677
今日推荐