1 字符串算术表达式分类

字符串算术表达式分为前缀表达式、中缀表达式和后缀表达式。其中前缀表达式又称波兰表达式,后缀表达式基于前缀表达式,又称逆波兰表达式。下面给出百度百科关于几种表达式的定义:

前缀表达式是一种没有括号的算术表达式,与中缀表达式不同的是,其将运算符写在前面,操作数写在后面。为纪念其发明者波兰数学家Jan Lukasiewicz,前缀表达式也称为“波兰式”。例如,- 1 + 2 3,它等价于1-(2+3)。前缀表达式就是前序表达式,是一种是由波兰数学家扬·武卡谢维奇1920年引入的数学表达式方式。

中缀表达式(或中缀记法)是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。

逆波兰式(Reverse Polish Notation,RPN,或逆波兰记法),也叫后缀表达式(将运算符写在操作数之后)。如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+。

  • 前缀或者后缀表达式的作用:实现逆波兰式算法,难度并不大,但为什么要将看似简单的中缀表达式转换为复杂的逆波兰式?原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

2 前缀表达式

2.1 运算优势

前缀表达式是一种十分有用的表达式,将中缀表达式转换为前缀表达式后,就可以只依靠出栈、入栈两种简单操作完全解决中缀表达式的全部运算。

2.2 求值算法

对前缀表达式求值,要从右至左扫描表达式,首先从右边第一个字符开始判断,若当前字符是数字则一直到数字串的末尾再记录下来,若为运算符,则将右边离得最近的两个“数字串”作相应运算,然后以此作为一个新的“数字串”并记录下来;扫描到表达式最左端时扫描结束,最后运算的值即为表达式的值。

例如:对前缀表达式“- 1 + 2 3”求值,扫描到3时,记录下这个数字串,扫描到2时,记录下这个数字串,当扫描到+时,将+右移做相邻两数字串的运算符,记为2+3,结果为5,记录下5这个新数字串,然后继续向左扫描,扫描到1时,记录下这个数字串,扫描到-时,将-右移做相邻两数字串的运算符,记为1-5,结果为-4,此时关于这个表达式的全部运算已完成,故表达式的值为-4。

2.3 中缀转前缀算法

(1) 首先构造一个运算符栈(也可放置括号),运算符(以括号为分界点)在栈内遵循越往栈顶优先级不降低的原则进行排列。

(2)从右至左扫描中缀表达式,从右边第一个字符开始判断:

如果当前字符是数字,则分析到数字串的结尾并将数字串直接输出。

如果是运算符,则比较优先级。如果当前运算符的优先级大于等于栈顶运算符的优先级(当栈顶是括号时,直接入栈),则将运算符直接入栈;否则将栈顶运算符出栈并输出,直到当前运算符的优先级大于等于栈顶运算符的优先级(当栈顶是括号时,直接入栈),再将当前运算符入栈。

如果是括号,则根据括号的方向进行处理。如果是右的括号,则直接入栈;否则,遇左括号前将所有的运算符全部出栈并输出,遇右括号后将左、向右的两括号一起出栈(并不输出)。

(3) 重复上述操作(2)直至扫描结束,将栈内剩余运算符全部出栈并输出,再逆缀输出字符串。中缀表达式也就转换为前缀表达式了。

2.3 中缀转前缀算法代码实现

/*** 中缀表达式转前缀表达式* @param expression    中缀表达式* @return              前缀表达式*/
private String toPrefix(String expression) {final Stack<Character> opStack = new Stack<>();opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低char[] arr = expression.toCharArray();int len = arr.length;int currentIndex = len - 1 ;// 当前字符的位置int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值char currentOp, peekOp;// 当前操作符和栈顶操作符// 自由向左扫描表达式for (int i = len - 1; i >= 0; i--,currentIndex--) {currentOp = arr[i];if (isOperator(currentOp)) {// 如果当前字符是运算符if (count > 0) {stack.push(new String(arr, currentIndex + 1, count));// 取两个运算符之间的数字}peekOp = opStack.peek();if (currentOp == '(') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号while (opStack.peek() != ')') {stack.push(String.valueOf(opStack.pop()));}opStack.pop();} else {while (currentOp != ')' && peekOp != ',' && peekOp !=')' &&comparePrefix(currentOp, peekOp)) {stack.push(String.valueOf(opStack.pop()));peekOp = opStack.peek();}opStack.push(currentOp);}count = 0;} else {count++;}}if (count > 1 || (count == 1 && !isOperator(arr[currentIndex + 1]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中stack.push(new String(arr, currentIndex + 1, count));}while (opStack.peek() != ',') {// 将操作符栈中的剩余的元素添加到后缀式栈中stack.push(String.valueOf(opStack.pop()));}StringBuilder sb = new StringBuilder();stack.forEach(sb::append);return sb.toString();
}

2.4 前缀表达式求值代码实现

/*** 计算前缀表达式的值** @param expression 前缀表达式* @return 计算结果*/
private double calculatePrefix(String expression) {// 中缀表达式转前缀表达式convert(transform(expression), ExpressionType.PREFIX);// 反转栈Collections.reverse(stack);Stack<String> resultStack = new Stack<>();String firstValue = null, secondValue = null, currentOp;// 参与计算的第一个值,第二个值和算术运算符while (false == stack.isEmpty()) {currentOp = stack.pop();if (false == isOperator(currentOp.charAt(0))) {// 如果不是运算符则存入操作数栈中currentOp = currentOp.replace("~", "-");resultStack.push(currentOp);} else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算firstValue = resultStack.pop();secondValue = resultStack.pop();// 将负数标记符改为负号firstValue = firstValue.replace("~", "-");secondValue = secondValue.replace("~", "-");BigDecimal tempResult = calculate(firstValue, secondValue, currentOp.charAt(0));resultStack.push(tempResult.toString());}}return Double.parseDouble(resultStack.pop());
}

3 中缀表达式

就是我们平常书写的表达式形式,不在赘述。

4 后缀表达式

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

4.1 中缀转后缀算法

将一个普通的中缀表达式转换为逆波兰表达式的一般算法是:

首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为存放结果(逆波兰式)的栈S2(空栈),S1栈可先放入优先级最低的运算符#,注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:

(1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈。

(2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符(不包括括号运算符)优先级高于S1栈栈顶运算符(包括左括号)优先级,则将该运算符进S1栈,否则,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符(包括左括号)低于(不包括等于)该运算符优先级时停止弹出运算符,最后将该运算符送入S1栈。

(3)若取出的字符是“(”,则直接送入S1栈顶。

(4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。

(5)重复上面的1~4步,直至处理完所有的输入字符。

(6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。

完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!

4.2 中缀转后缀代码实现

/*** 中缀表达式转后缀表达式* @param expression    中缀表达式* @return              后缀表达式*/
private String toPostfix(String expression) {final Stack<Character> opStack = new Stack<>();opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低char[] arr = expression.toCharArray();int currentIndex = 0;// 当前字符的位置int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值char currentOp, peekOp;// 当前操作符和栈顶操作符for (int i = 0; i < arr.length; i++) {currentOp = arr[i];if (isOperator(currentOp)) {// 如果当前字符是运算符if (count > 0) {stack.push(new String(arr, currentIndex, count));// 取两个运算符之间的数字}peekOp = opStack.peek();if (currentOp == ')') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号while (opStack.peek() != '(') {stack.push(String.valueOf(opStack.pop()));}opStack.pop();} else {while (currentOp != '(' && peekOp != ',' &&  peekOp != '(' &&comparePostfix(currentOp, peekOp)) {stack.push(String.valueOf(opStack.pop()));peekOp = opStack.peek();}opStack.push(currentOp);}count = 0;currentIndex = i + 1;} else {count++;}}if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中stack.push(new String(arr, currentIndex, count));}while (opStack.peek() != ',') {// 将操作符栈中的剩余的元素添加到后缀式栈中stack.push(String.valueOf(opStack.pop()));}StringBuilder sb = new StringBuilder();stack.forEach(sb::append);return sb.toString();
}

4.3 后缀求值算法

新建一个表达式,如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

4.4 后缀求值实现

/*** 计算后缀表达式的值** @param expression 后缀表达式* @return 计算结果*/
private double calculatePostfix(String expression) {// 中缀转后缀convert(transform(expression), ExpressionType.POSTFIX);// 反正后缀栈Collections.reverse(stack);Stack<String> resultStack = new Stack<>();String firstValue, secondValue , currentOp;// 参与计算的第一个值,第二个值和算术运算符while (false == stack.isEmpty()) {currentOp = stack.pop();if (false == isOperator(currentOp.charAt(0))) {// 如果不是运算符则存入操作数栈中currentOp = currentOp.replace("~", "-");resultStack.push(currentOp);} else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算secondValue = resultStack.pop();firstValue = resultStack.pop();// 将负数标记符改为负号firstValue = firstValue.replace("~", "-");secondValue = secondValue.replace("~", "-");BigDecimal tempResult = calculate(firstValue, secondValue, currentOp.charAt(0));resultStack.push(tempResult.toString());}}return Double.parseDouble(resultStack.pop());
}

5 基于栈的简单计算器实现

此实现的参考cn.hutool.core.math.Calculator,其中hutool使用的是后缀表达式的方式;我们的实现也给出了前缀表达式的计算方式,默认使用后缀表达式形式。

完整如下5-1代码所示:也可以去后面仓库代码中查看完整代码及测试代码

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;import java.math.BigDecimal;
import java.util.Collections;
import java.util.Stack;/*** @author Administrator* @version 1.0* @description 简单计算器* @date 2022-10-31 20:26*/
public class Calculator {/*** 表达式栈*/private final Stack<String> stack = new Stack<>();///*** 运用运算符ASCII码-40做索引的运算符优先级,%单独判断优先级和*,/同级*/private final int[] OperatorPriorities = new int[]{4, 4, 2, 1, -1, 1, 3, 2};/*** 计算表达式的值** @param expression 表达式* @return 计算结果*/public  double execute(String expression, ExpressionType type) {if (type == null || ExpressionType.POSTFIX.equals(type)) {return calculatePostfix(expression);} else if (type.equals(ExpressionType.PREFIX)) {return calculatePrefix(expression);} else {throw new IllegalStateException("Unexpected type: " + type);}}/*** 计算表达式的值** @param expression 表达式* @return 计算结果*/public  double execute(String expression) {return calculatePostfix(expression);}/*** 计算后缀表达式的值** @param expression 后缀表达式* @return 计算结果*/private double calculatePostfix(String expression) {// 中缀转后缀convert(transform(expression), ExpressionType.POSTFIX);// 反正后缀栈Collections.reverse(stack);Stack<String> resultStack = new Stack<>();String firstValue, secondValue , currentOp;// 参与计算的第一个值,第二个值和算术运算符while (false == stack.isEmpty()) {currentOp = stack.pop();if (false == isOperator(currentOp.charAt(0))) {// 如果不是运算符则存入操作数栈中currentOp = currentOp.replace("~", "-");resultStack.push(currentOp);} else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算secondValue = resultStack.pop();firstValue = resultStack.pop();// 将负数标记符改为负号firstValue = firstValue.replace("~", "-");secondValue = secondValue.replace("~", "-");BigDecimal tempResult = calculate(firstValue, secondValue, currentOp.charAt(0));resultStack.push(tempResult.toString());}}return Double.parseDouble(resultStack.pop());}/*** 计算前缀表达式的值** @param expression 前缀表达式* @return 计算结果*/private double calculatePrefix(String expression) {// 中缀表达式转前缀表达式convert(transform(expression), ExpressionType.PREFIX);// 反转栈Collections.reverse(stack);Stack<String> resultStack = new Stack<>();String firstValue = null, secondValue = null, currentOp;// 参与计算的第一个值,第二个值和算术运算符while (false == stack.isEmpty()) {currentOp = stack.pop();if (false == isOperator(currentOp.charAt(0))) {// 如果不是运算符则存入操作数栈中currentOp = currentOp.replace("~", "-");resultStack.push(currentOp);} else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算firstValue = resultStack.pop();secondValue = resultStack.pop();// 将负数标记符改为负号firstValue = firstValue.replace("~", "-");secondValue = secondValue.replace("~", "-");BigDecimal tempResult = calculate(firstValue, secondValue, currentOp.charAt(0));resultStack.push(tempResult.toString());}}return Double.parseDouble(resultStack.pop());}/*** 数据准备阶段将表达式转换为指定类型的表达式** @param expression 表达式* @param type       表达式类型*/public String convert(String expression, ExpressionType type) {if (type == null || ExpressionType.POSTFIX.equals(type)) {return toPostfix(expression);} else if (type.equals(ExpressionType.PREFIX)) {return toPrefix(expression);} else {throw new IllegalStateException("Unexpected type: " + type);}}/*** 数据准备阶段将表达式转换成为后缀式栈** @param expression 表达式*/public String convert(String expression) {return toPostfix(expression);}/*** 中缀表达式转前缀表达式* @param expression    中缀表达式* @return              前缀表达式*/private String toPrefix(String expression) {final Stack<Character> opStack = new Stack<>();opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低char[] arr = expression.toCharArray();int len = arr.length;int currentIndex = len - 1 ;// 当前字符的位置int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值char currentOp, peekOp;// 当前操作符和栈顶操作符// 自由向左扫描表达式for (int i = len - 1; i >= 0; i--,currentIndex--) {currentOp = arr[i];if (isOperator(currentOp)) {// 如果当前字符是运算符if (count > 0) {stack.push(new String(arr, currentIndex + 1, count));// 取两个运算符之间的数字}peekOp = opStack.peek();if (currentOp == '(') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号while (opStack.peek() != ')') {stack.push(String.valueOf(opStack.pop()));}opStack.pop();} else {while (currentOp != ')' && peekOp != ',' && peekOp !=')' &&comparePrefix(currentOp, peekOp)) {stack.push(String.valueOf(opStack.pop()));peekOp = opStack.peek();}opStack.push(currentOp);}count = 0;} else {count++;}}if (count > 1 || (count == 1 && !isOperator(arr[currentIndex + 1]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中stack.push(new String(arr, currentIndex + 1, count));}while (opStack.peek() != ',') {// 将操作符栈中的剩余的元素添加到后缀式栈中stack.push(String.valueOf(opStack.pop()));}StringBuilder sb = new StringBuilder();stack.forEach(sb::append);return sb.toString();}/*** 中缀表达式转后缀表达式* @param expression    中缀表达式* @return              后缀表达式*/private String toPostfix(String expression) {final Stack<Character> opStack = new Stack<>();opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低char[] arr = expression.toCharArray();int currentIndex = 0;// 当前字符的位置int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值char currentOp, peekOp;// 当前操作符和栈顶操作符for (int i = 0; i < arr.length; i++) {currentOp = arr[i];if (isOperator(currentOp)) {// 如果当前字符是运算符if (count > 0) {stack.push(new String(arr, currentIndex, count));// 取两个运算符之间的数字}peekOp = opStack.peek();if (currentOp == ')') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号while (opStack.peek() != '(') {stack.push(String.valueOf(opStack.pop()));}opStack.pop();} else {while (currentOp != '(' && peekOp != ',' &&  peekOp != '(' &&comparePostfix(currentOp, peekOp)) {stack.push(String.valueOf(opStack.pop()));peekOp = opStack.peek();}opStack.push(currentOp);}count = 0;currentIndex = i + 1;} else {count++;}}if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中stack.push(new String(arr, currentIndex, count));}while (opStack.peek() != ',') {// 将操作符栈中的剩余的元素添加到后缀式栈中stack.push(String.valueOf(opStack.pop()));}StringBuilder sb = new StringBuilder();stack.forEach(sb::append);return sb.toString();}/*** 判断是否为算术符号** @param c 字符* @return 是否为算术符号*/private boolean isOperator(char c) {return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')' || c == '%';}/*** 利用ASCII码-40做下标去算术符号优先级** @param cur  下标* @param peek peek* @return 优先级,如果cur高或相等,返回true,否则false*/private boolean comparePostfix(char cur, char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低final int offset = 40;if(cur  == '%'){// %优先级最高cur = 47;}if(peek  == '%'){// %优先级最高peek = 47;}return OperatorPriorities[peek - offset] >= OperatorPriorities[cur - offset];}private boolean comparePrefix(char cur, char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低final int offset = 40;if(cur  == '%'){// %优先级最高cur = 47;}if(peek  == '%'){// %优先级最高peek = 47;}return OperatorPriorities[peek - offset] > OperatorPriorities[cur - offset];}/*** 按照给定的算术运算符做计算** @param firstValue  第一个值* @param secondValue 第二个值* @param currentOp   算数符,只支持'+'、'-'、'*'、'/'、'%'* @return 结果*/private BigDecimal calculate(String firstValue, String secondValue, char currentOp) {BigDecimal result;switch (currentOp) {case '+':result = NumberUtil.add(firstValue, secondValue);break;case '-':result = NumberUtil.sub(firstValue, secondValue);break;case '*':result = NumberUtil.mul(firstValue, secondValue);break;case '/':result = NumberUtil.div(firstValue, secondValue);break;case '%':result = NumberUtil.toBigDecimal(firstValue).remainder(NumberUtil.toBigDecimal(secondValue));break;default:throw new IllegalStateException("Unexpected value: " + currentOp);}return result;}/*** 将表达式中负数的符号更改** @param expression 例如-2+-1*(-3E-2)-(-1) 被转为 ~2+~1*(~3E~2)-(~1)* @return 转换后的字符串*/private static String transform(String expression) {expression = StrUtil.cleanBlank(expression);expression = StrUtil.removeSuffix(expression, "=");final char[] arr = expression.toCharArray();for (int i = 0; i < arr.length; i++) {if (arr[i] == '-') {if (i == 0) {arr[i] = '~';} else {char c = arr[i - 1];if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == 'E' || c == 'e') {arr[i] = '~';}}}}if (arr[0] == '~' && (arr.length > 1 && arr[1] == '(')) {arr[0] = '-';return "0" + new String(arr);} else {return new String(arr);}}
}

7 思考

下面留下一些问题小伙伴来回答下:

  1. 我们实现的中缀转后缀与hutool中实现有什么不同?

    • 提示:一个判断条件
  2. 我们实现的前缀计算和后缀计算有什么不同之处?
    • 提示:与中缀转相应表达式时读取原中缀表达式顺序有关
  3. 如何用我们自己实现的栈结构替换JDK中的Stack呢?

欢迎小伙伴下方留言讨论。

6 后记

如果小伙伴什么问题或者指教,欢迎交流。

❓QQ:806797785

⭐️源代码仓库地址:https://gitee.com/gaogzhen/algorithm

参考:

[1]百度百科.<a https://baike.baidu.com/item/%E5%89%8D%E7%BC%80%E8%A1%A8%E8%BE%BE%E5%BC%8F">前缀表达式[EB/OL].2022-09-19/2022-11-02.

[2]百度百科.逆波兰式[EB/OL].2022-09-19/2022-11-02.

[3]gitee.cn.hutool.core.math.Calculator[EB/OL].2022-10-02/2022-11-02.

字符串算术表达式求值-简单计算器实现(栈)-数据结构和算法(Java)相关推荐

  1. 算术表达式求值(C语言栈)

    题目:算术表达式求值 题目描述:表达式计算是实现程序设计语言的基本问题之一,也是栈的应用的一个典型例子.设计一个程序,演示用运算符优先法对算数表达式求值的过程. 基本要求:以字符序列的形式从终端输入语 ...

  2. 算术表达式求值的程序设计与实现_数据结构课程设计

    以下内容可且仅可供参考,如有错误欢迎指正. 部分思路借鉴算术表达式求值(C语言栈)_夜何其的博客-CSDN博客_c语言利用栈求解算术表达式侵删致歉 <算术表达式求值的程序设计与实现>题目要 ...

  3. python 表达式求值_简单算术表达式求值

    本文主要探讨简单的数学算术表达式求值算法的原理和实现. 1. 约束 本文只是探讨简单的算术表达式的求值算法,为了将主要精力放在算法思想的探讨和实现上,避免陷入对其他不是直接相关的细节的过多思考,所以提 ...

  4. boost::proto模块实现简单的算术表达式求值器的测试程序

    boost::proto模块实现简单的算术表达式求值器的测试程序 实现功能 C++实现代码 实现功能 boost::proto模块实现简单的算术表达式求值器的测试程序 C++实现代码 #include ...

  5. 信息学奥赛一本通 1397:简单算术表达式求值 | OpenJudge NOI 1.12 01:简单算术表达式求值

    [题目链接] ybt 1397:简单算术表达式求值 OpenJudge NOI 1.12 01:简单算术表达式求值 [题目考点] 1. 函数 2. 选择结构 [解题思路] 这一章节都是练习函数,那么这 ...

  6. 信息学奥赛一本通(1397:简单算术表达式求值)

    1397:简单算术表达式求值 时间限制: 1000 ms         内存限制: 65536 KB 提交数: 13874     通过数: 10359 [题目描述] 两位正整数的简单算术运算(只考 ...

  7. 《Algorithms》—— Dijkstra 的双栈算术表达式求值算法

    想当年学数据结构的时候,一直觉得这个是我一辈子都搞不懂的一个东西.现在看看...还挺简单的... 重点在于如何解析由括号.运算符和数字组成的字符串,并按照正确的顺序完成各种初级算术操作.利用了两个栈( ...

  8. 用算符优先法对算术表达式求值(六)

    18.11.23 这是一道最近刚上的实验课的题目.... 基于C语言,欢迎指正 实验要求 掌握栈在解决实际问题中的应用,设计一个程序,演算用算符优先法对算术表达式求值的过程,利用算符优先关系,实现对算 ...

  9. 【Java】基于栈的算术表达式求值

    定义异常类 public class ExpressionException extends RuntimeException {private static final long serialVer ...

最新文章

  1. HTML实现折现图完整源码及效果图
  2. [学习笔记]stm32
  3. 与容器服务 ACK 发行版的深度对话第二弹:如何借助 hybridnet 构建混合云统一网络平面
  4. unity热更新json_Unity热更新之AssetBundle打包篇
  5. drools 7.x-复杂事件处理入门
  6. 买不到口罩怎么办?Python 爬虫帮你时刻盯着自动下单!| 原力计划
  7. 如何删除“运行”里面的内容
  8. 促进大数据发展行动纲要
  9. C++基础之detele和detele[]
  10. android检测蜂窝网络,怎么检查您的Android设备蜂窝信号强度?
  11. php 合并多个pdf,快速查阅多个PDF文件,这2个PDF合并技巧要学会!
  12. git提交代码失败 ‘“node“‘ �����ڲ����ⲿ���Ҳ���ǿ����еij������������ļ��� 解决方法
  13. mac之间迁移微信聊天记录
  14. 不懂中医的才攻击中医
  15. PDF怎么拆分/合并? 3款 PDF 拆分和合并工具分享
  16. Costco市值10年增长5倍的秘诀:水坝式经营
  17. 时间序列-异常检测(Anomaly Detection)(二):传统方法
  18. Qt问题:中文乱码+常量中有换行符
  19. 本地RTMP流媒体服务器搭建拉流简易版
  20. 2组语法,1个函数,教你学会用Python做数据分析!

热门文章

  1. 香港IT人的工作情况~
  2. match_phrase短语匹配和近似匹配
  3. 教育论文中的论证方式
  4. 如何下载阿里妈妈订单EXCEL(cookie保持)
  5. 关于java异常处理机制的深入理解
  6. 1.Hibernate框架核心组件 (转自冯岩)
  7. 单位的换算 (Python)
  8. Windows 2003开多用户及终端授权激活详细教程
  9. 完全删除SQL SERVER
  10. 润乾报表-时间日期函数