表达式计算 Java算法
2017-11-30 23:51
162 查看
一个关于带括号的四则运算简单算法,看题目
题目分析:
简单地说,题目就是要求我们写一个带有括号的四则运算算法.
我们先来以人脑的思维做一下这种四则运算:
首先, 看到+和-可以先忽略, 看到*(乘法)和/(除法)也可以先放一边, 优先找表达式里面有没有()括号(因为四则运算的优先级是规定好的嘛).
根据上述的规则, 在做 1-2+3*(4-5) 这道题目时, 优先执行小括号里面的内容 (4-5)=-1 , 表达式变成 1-2+3*(-1) , 然后乘除法优先于加减法, 执行 3*(-1)=-3 , 表达式变成 1-2+(-3) , 最后就是加减的平级运算, 依次计算后可以得到最终结果 -4.
其实这种四则运算小学就会了, 有的人看了上面的步骤可能都不耐烦了, 但是别急, 跟着思路来.
思路分析:
通过人脑思维做四则运算时, 我们发现其实判断的只有三个东西.
第一个就是进行运算的数字, 我们可以称其为 操作数
第二个是运算的符号(加减乘除), 可以称其为 操作符
第三个是判断是否有小括号, 有的话就要优先执行里面的内容, 其实也可以归类为上面的 操作符
除此之外, 我们在运算的时候, 是从左到右读取整个表达式的
碰到加减号是优先忽略的, 优先计算的是乘除法和小括号里面的内容, 计算完之后, 再回过头计算加减法的
需要的技术分析:
通过思路分析我们发现, 计算的顺序是从左到右, 碰到加减法时,可以想象是把加减号和相关的数字先存入一个盒子中, 碰到乘除法时, 如果乘除号后面不是括号, 那么直接计算, 如果是括号, 那么同样的存入一个盒子中, 如果碰到括号了, 那么直接计算括号里面的表达式, 最后回过头, 取出盒子里面的数字和符号平级运算.
根据存入盒子这种做法, 优先计算的是后面的数字, 我们可以想到利用 栈 来帮助我们. 为什么要用栈呢, 请看下面的图示 :
代码中的技术需求分析:
用两个集合模拟栈空间, 每次取出的时候从尾部取出(从栈顶弹栈), 一个集合存储数字, 另一个存储 运算符和前括号.
三个全局静态变量:
一个操作符标记变量 op, 用于标记本次运算的类型是加减乘除中的哪一个, 因为测试环境是jdk1.6, 还不支持在switch中传入字符串, 所以使用标记位进行标记, 如果你的jdk是1.7及其以上, 那么可以不需要这个标记位, 而是直接传入字符进行判断.
一个字符串sf, 用于保存每次获取到的一个字符, 用于接下来的判断.
一个可变字符串sb, 用于保存单独的一整个数字.
四个计算和判断的方法:
截取字符进行判断操作的方法operating : 该方法每次截取一个字符, 如果是操作数, 那么存入可变字符串sb,当获取到了完整的一个独立数时,存入操作数集合中 ; 如果是操作符, 若不是反括号, 那么直接存入操作符集合, 若是反括号, 那么调用反括号的方法 ; 最后, 当输入的表达式截取完毕后, 判断, 如果操作数集合只剩下一个元素, 说明计算结果已经得到了, 结束方法即可, 否则, 调用计算方法进行四则运算.
四则运算计算方法operationStart : 根据获取到的两个操作数a , b 和一个操作符 op 进行判断对应的运算, 然后返回运算结果result .
为反括号的情况设计的特殊方法fanKuoHao : 因为是反括号, 意味着已经到了一对括号的尾部, 需要优先执行里面的表达式, 所以此时不把反括号压栈存储, 而是 直接取出两个操作数和一个操作符进行计算, 然后将结果压栈存储, 一直重复这个操作, 直到操作符栈顶元素是前括号时, 将前括号弹出, 然后结束方法.
弹出两个操作数和一个操作符的方法getTwoAndOne : 弹出两个操作数和一个操作符, 然后将参数传入四则运算计算方法operationStart , 得到计算结果后, 判断此时操作符栈顶的元素是否是减号, 如果是, 那么将结果取反然后压入操作数栈顶, 然后将操作符栈顶的减号改变为加号, 否则结束方法.
最后看一下大致的流程图:
注意: 根据这个算法只能进行简单的带括号的四则运算, 因为我自己也思考了其他的几种情况, 例如这些情况 : -(-(-(-(-(-16))))) , -(-5-2)-(-(-7)) , 这种表达式的难点就在于负号的判断, 头部的负号判断很容易, 其他位置的就有一定的难度了, 这部分功能就留给读者自己思考实现吧, 另外, 通常情况也不会计算这种”变态的表达式”.
最后代码如下(Java):
题目分析:
简单地说,题目就是要求我们写一个带有括号的四则运算算法.
我们先来以人脑的思维做一下这种四则运算:
首先, 看到+和-可以先忽略, 看到*(乘法)和/(除法)也可以先放一边, 优先找表达式里面有没有()括号(因为四则运算的优先级是规定好的嘛).
根据上述的规则, 在做 1-2+3*(4-5) 这道题目时, 优先执行小括号里面的内容 (4-5)=-1 , 表达式变成 1-2+3*(-1) , 然后乘除法优先于加减法, 执行 3*(-1)=-3 , 表达式变成 1-2+(-3) , 最后就是加减的平级运算, 依次计算后可以得到最终结果 -4.
其实这种四则运算小学就会了, 有的人看了上面的步骤可能都不耐烦了, 但是别急, 跟着思路来.
思路分析:
通过人脑思维做四则运算时, 我们发现其实判断的只有三个东西.
第一个就是进行运算的数字, 我们可以称其为 操作数
第二个是运算的符号(加减乘除), 可以称其为 操作符
第三个是判断是否有小括号, 有的话就要优先执行里面的内容, 其实也可以归类为上面的 操作符
除此之外, 我们在运算的时候, 是从左到右读取整个表达式的
碰到加减号是优先忽略的, 优先计算的是乘除法和小括号里面的内容, 计算完之后, 再回过头计算加减法的
需要的技术分析:
通过思路分析我们发现, 计算的顺序是从左到右, 碰到加减法时,可以想象是把加减号和相关的数字先存入一个盒子中, 碰到乘除法时, 如果乘除号后面不是括号, 那么直接计算, 如果是括号, 那么同样的存入一个盒子中, 如果碰到括号了, 那么直接计算括号里面的表达式, 最后回过头, 取出盒子里面的数字和符号平级运算.
根据存入盒子这种做法, 优先计算的是后面的数字, 我们可以想到利用 栈 来帮助我们. 为什么要用栈呢, 请看下面的图示 :
代码中的技术需求分析:
用两个集合模拟栈空间, 每次取出的时候从尾部取出(从栈顶弹栈), 一个集合存储数字, 另一个存储 运算符和前括号.
三个全局静态变量:
一个操作符标记变量 op, 用于标记本次运算的类型是加减乘除中的哪一个, 因为测试环境是jdk1.6, 还不支持在switch中传入字符串, 所以使用标记位进行标记, 如果你的jdk是1.7及其以上, 那么可以不需要这个标记位, 而是直接传入字符进行判断.
一个字符串sf, 用于保存每次获取到的一个字符, 用于接下来的判断.
一个可变字符串sb, 用于保存单独的一整个数字.
四个计算和判断的方法:
截取字符进行判断操作的方法operating : 该方法每次截取一个字符, 如果是操作数, 那么存入可变字符串sb,当获取到了完整的一个独立数时,存入操作数集合中 ; 如果是操作符, 若不是反括号, 那么直接存入操作符集合, 若是反括号, 那么调用反括号的方法 ; 最后, 当输入的表达式截取完毕后, 判断, 如果操作数集合只剩下一个元素, 说明计算结果已经得到了, 结束方法即可, 否则, 调用计算方法进行四则运算.
四则运算计算方法operationStart : 根据获取到的两个操作数a , b 和一个操作符 op 进行判断对应的运算, 然后返回运算结果result .
为反括号的情况设计的特殊方法fanKuoHao : 因为是反括号, 意味着已经到了一对括号的尾部, 需要优先执行里面的表达式, 所以此时不把反括号压栈存储, 而是 直接取出两个操作数和一个操作符进行计算, 然后将结果压栈存储, 一直重复这个操作, 直到操作符栈顶元素是前括号时, 将前括号弹出, 然后结束方法.
弹出两个操作数和一个操作符的方法getTwoAndOne : 弹出两个操作数和一个操作符, 然后将参数传入四则运算计算方法operationStart , 得到计算结果后, 判断此时操作符栈顶的元素是否是减号, 如果是, 那么将结果取反然后压入操作数栈顶, 然后将操作符栈顶的减号改变为加号, 否则结束方法.
最后看一下大致的流程图:
注意: 根据这个算法只能进行简单的带括号的四则运算, 因为我自己也思考了其他的几种情况, 例如这些情况 : -(-(-(-(-(-16))))) , -(-5-2)-(-(-7)) , 这种表达式的难点就在于负号的判断, 头部的负号判断很容易, 其他位置的就有一定的难度了, 这部分功能就留给读者自己思考实现吧, 另外, 通常情况也不会计算这种”变态的表达式”.
最后代码如下(Java):
import java.util.ArrayList; import java.util.List; import java.util.Scanner; /* * 1.用String存储输入的算术表达式, 每次获取一个字符 * 2.判断获取到的字符, 以下几种情况依次处理: * 如果是数字, 那么存入可变字符串; * 如果是操作符,只要字符不是反括号 ")", 那么都压入对应的栈; * 若字符串取完了, 则每次取两个操作数和一个运算符,计算后将结果压栈,如此循环,直到操作数栈剩一个数. * 3.用两个集合模拟两个栈空间, 一个栈空间用于保存操作数, 另一个栈空间用于保存运算符 * 4.定义四则运算的方法 * 5.定义反括号特殊方法 * 6.定义操作字符串方法 * 7.定义弹出操作数和操作符的方法 * 8.由于测试机要求用jdk1.6, 而1.6中的switch语句还不支持String判断(1.7开始才支持),所以用代号代替运算符 * +: 1 -: 2 *: 3 /: 4 */ public class Main { // 临时变量标记该运算类型 static int op = -1; // 保存每次获取到的当前字符 static String sf = ""; //存储独立的数字 static StringBuffer sb = new StringBuffer(); public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 操作数栈 List<Integer> operands = new ArrayList<Integer>(); // 运算符栈 List<String> operator = new ArrayList<String>(); // 字符串接收输入的字符 String str = sc.next(); // 调用判断的方法对该字符串进行操作 operating(str, operands, operator); // 最后输出运算结果, 加入了一个判断头部是否是负号的判断 if (! bbb3 operator.isEmpty() && operator.get(0).matches("[-]")) { System.out.println(-operands.get(0)); } else { System.out.println(operands.get(0)); } } // 操作字符串的方法 public static void operating(String str, List<Integer> operands, List<String> operator) { // 每次截取一个字符进行判断 for (int i = 0; i < str.length(); i++) { // 如果是数字那么存入可变字符串 sf = str.substring(i, i + 1); //拼接数字 if (sf.matches("[0-9]")) { sb.append(sf); //下一个字符不是数字,或者这是最后一个数,那么存入集合,然后清空sb if(i == str.length()-1 || !str.substring(i+1, i+2).matches("[0-9]")){ operands.add(Integer.parseInt(sb.toString())); sb.delete(0, sb.length()); // 因为压入的是数字,所以判断前面的操作符是否是乘法*或除法/,如果是,那么取出两个操作数和一个操作符计算结果然后压入操作数栈 if (!operator.isEmpty() && operator.get(operator.size() - 1).matches("[/*//]")) { getTwoAndOne(operands, operator); } } } else if (sf.matches("[/+-/*(]")) { // 如果字符是除了数字和反括号以外的,那么直接压入操作符栈 operator.add(sf); }else { // 如果字符是反括号,那么调用反括号的运行方法(即取出两个操作数和一个操作符的方法) fanKuoHao(operands, operator); } } // 字符串截取完毕, 两个栈里面剩下的都是+或者-的平级运算, 用循环运算完毕即可 while (true) { // 操作数栈剩余一个数时, 结束算法 if (operands.size() == 1) { break; } else { // 执行提取两个操作数和一个操作符的方法 getTwoAndOne(operands, operator); } } } // 四则运算方法,用switch语句进行判断,返回运算结果,传入两个操作数(注意先后顺序),和一个操作符 public static int operationStart(int a, int b, int op) { int result = 0; switch (op) { case 1: result = a + b; break; case 2: result = a - b; break; case 3: result = a * b; break; case 4: result = a / b; break; default: System.out.println("Error!"); } return result; } /* * 碰到反括号的运行方法: 碰到反括号, 直接取出两个操作数和一个操作符, 运算结果压入运算栈, 直到碰到运算符栈顶为前括号则结束方法 */ public static void fanKuoHao(List<Integer> operands, List<String> operator) { while (true) { if (operator.get(operator.size() - 1).matches("[(]")) { // 如果是前括号, 并且那么弹出前括号,并且结束方法 operator.remove(operator.size() - 1); break; } else { getTwoAndOne(operands, operator); } } } /* * 弹出两个数和一个操作符, 计算后将结果压入操作数栈 */ public static void getTwoAndOne(List<Integer> operands, List<String> operator) { // 判断操作符, 设置对应的op值 if (operator.get(operator.size() - 1).matches("[*]")) { op = 3; } else if (operator.get(operator.size() - 1).matches("[/]")) { op = 4; } else if (operator.get(operator.size() - 1).matches("[+]")) { op = 1; } else { op = 2; } // 最后加减平级运算时,要考虑前一个数是不是负数,如果是,那么取反,并且下一个操作数变成+ int a = operands.get(operands.size() - 2); int b = operands.get(operands.size() - 1); if (operator.size() > 1 && operator.get(operator.size() - 2).matches("[-]")) { a = -a; operator.set(operator.size() - 2, "+"); } int result = operationStart(a, b, op); operands.remove(operands.size() - 1); operands.remove(operands.size() - 1); operands.add(result); operator.remove(operator.size() - 1); } }
相关文章推荐
- 栈在表达式计算中的应用
- 数据结构--表达式计算
- 对ORA-01795: 列表中的最大表达式数为 1000的处理(算法:计算数量及切割)
- 算法训练 表达式计算
- 逆波兰表达式(后缀表达式)及其计算
- 表达式的计算(链栈表示)
- 数据结构—中缀表达式转后缀表达式算法及实现—栈的应用—计算表达式(C++代码实现)(1)
- Java实现表达式计算(中缀表达式转化为后缀表达式/逆波兰式)
- 逆波兰表达式计算
- ORACLE 数据库使用正则表达式重新计算指定位置的数字为新的数字
- 利用栈和队列计算带有括号的表达式(纯理论)
- 编译原理之后缀表达式生成与计算
- 中缀表达式的计算(只包含四则运算与括号)
- 二叉树表达规定形式的代数表达式并计算表达式结果(无明显缺陷版本)
- C语言printf函数输出表达式中的计算顺序
- 异常:由于代码已经过优化或者本机框架位于调用堆栈之上,无法计算表达式的值。
- java 运用表达式计算贷款金额
- 表达式计算
- 由于代码已经过优化或者本机框架位于调用堆栈之上,无法计算表达式的值。
- 输入一个只包含个位数字的简单四则运算表达式字符串,计算该表达式的值