Java描述表达式求值的两种解法:双栈结构和二叉树

原题大意:表达式求值

求一个非负整数四则混合运算且含嵌套括号表达式的值。如:

  1. # 输入:
  2. 1+2*(6/2)-4
  3. # 输出:
  4. 3.0

数据保证:

  1. 保证表达式合法(含除数不为0)。
  2. 保证运算数是非负整数。

双栈版

维护两个栈: 符号栈,数字栈,遍历输入串过程中计算

  1. 数字直接入栈
  2. 符号入栈

    a. 符号栈为空

    b. 当前符号优先于栈顶符号

    c. 栈顶为'('
  3. 符号出栈计算: 栈顶符号非'(' 且 优先级更高.
  1. class ExpStack {
  2. private static final String LEVEL_OPTS = ")+-*/(";
  3. public double solve(String input) {
  4. Stack<Character> optStack = new Stack<>();
  5. Stack<Double> numStack = new Stack<>();
  6. int i = 0;
  7. while (i < input.length()) {
  8. if (Character.isDigit(input.charAt(i))) { // 如果是数字直接入栈
  9. int t = 0;
  10. for (; i < input.length() && Character.isDigit(input.charAt(i)); i++) {
  11. t = t * 10 + input.charAt(i) - '0';
  12. }
  13. numStack.push((double) t);
  14. } else {// 如果是操作符
  15. //栈为空 或 当前优先于栈顶: 入栈
  16. if (optStack.isEmpty() || compPriority(input.charAt(i), optStack.peek()) > 0) {
  17. optStack.push(input.charAt(i++));
  18. } else if (optStack.peek() == '(') { // 栈顶为左括号 '('
  19. if (input.charAt(i) == ')') { // 括号完成: 弹出'('
  20. optStack.pop();
  21. } else { // 括号开始 : 入栈
  22. optStack.push(input.charAt(i));
  23. }
  24. i++;
  25. } else { // 栈顶优先级更高且非 '(' : 运算
  26. double top = numStack.pop();
  27. numStack.push(calc(numStack.pop(), optStack.pop(), top));
  28. }
  29. }
  30. }
  31. while (!optStack.isEmpty()) {
  32. double top = numStack.pop();
  33. numStack.push(calc(numStack.pop(), optStack.pop(), top));
  34. }
  35. return numStack.pop();
  36. }
  37. private int compPriority(char c1, char c2) {
  38. return LEVEL_OPTS.indexOf(c1) - LEVEL_OPTS.indexOf(c2);
  39. }
  40. private double calc(double x, char o, double y) {
  41. switch (o) {
  42. case '+':
  43. return x + y;
  44. case '-':
  45. return x - y;
  46. case '*':
  47. return x * y;
  48. case '/':
  49. return x / y;
  50. }
  51. return 0;
  52. }
  53. }

二叉树版

构建二叉树: 非叶子节点为符号,叶子节点为数字. 最终后序搜索计算

二分点: 表达式中最后一个计算的运算符

  1. 排除括号后
  2. 优先取 + | -
  3. 再考虑 * | /
  1. class ExpTree {
  2. private String mInput;
  3. private java.util.LinkedList<Node> mTree;
  4. public double solve(String input) {
  5. mInput = input;
  6. mTree = new java.util.LinkedList<>();
  7. buildTree(0, mInput.length());
  8. return dfs(mTree.size() - 1);
  9. }
  10. private int buildTree(int li, int ri) {
  11. try { // 先尝试吧表达式解析为叶子节点(纯运算数)
  12. int n = Integer.parseInt(mInput.substring(li, ri));
  13. Node node = new Node(n, -1, -1);
  14. mTree.addLast(node);
  15. return mTree.size() - 1;
  16. } catch (Exception ignore) {
  17. }
  18. // 找到最外层的运算符(最后一个计算的运算符,优先级最低的符号)
  19. int opt, as = -1, md = -1, bracket = 0;
  20. for (int i = li; i < ri; i++) {
  21. switch (mInput.charAt(i)) {
  22. case '(':
  23. bracket++;
  24. break;
  25. case ')':
  26. bracket--;
  27. break;
  28. case '+':
  29. case '-':
  30. if (bracket == 0) {
  31. as = i;
  32. }
  33. break;
  34. case '*':
  35. case '/':
  36. if (bracket == 0) {
  37. md = i;
  38. }
  39. break;
  40. }
  41. }
  42. opt = as < 0 ? md : as;
  43. if (opt < 0) { // 发现这是一个被括号包裹的表达式(去掉括号重新构造)
  44. return buildTree(li + 1, ri - 1);
  45. }
  46. // 依次构造左右子树
  47. Node node = new Node(mInput.charAt(opt), buildTree(li, opt), buildTree(opt + 1, ri));
  48. mTree.addLast(node);
  49. return mTree.size() - 1;
  50. }
  51. private double dfs(int i) { // 后序遍历求解
  52. if (mTree.get(i).lch == -1 && mTree.get(i).rch == -1) {
  53. return mTree.get(i).num;
  54. }
  55. switch (mTree.get(i).opt) {
  56. case '+':
  57. return dfs(mTree.get(i).lch) + dfs(mTree.get(i).rch);
  58. case '-':
  59. return dfs(mTree.get(i).lch) - dfs(mTree.get(i).rch);
  60. case '*':
  61. return dfs(mTree.get(i).lch) * dfs(mTree.get(i).rch);
  62. case '/':
  63. return dfs(mTree.get(i).lch) / dfs(mTree.get(i).rch);
  64. }
  65. return 0;
  66. }
  67. private static class Node {
  68. double num;
  69. char opt;
  70. int lch, rch;
  71. Node(double n, int l, int r) {
  72. num = n;
  73. initChild(l, r);
  74. }
  75. Node(char o, int l, int r) {
  76. opt = o;
  77. initChild(l, r);
  78. }
  79. private void initChild(int l, int r) {
  80. lch = l;
  81. rch = r;
  82. }
  83. }
  84. }

测试驱动函数

  1. public class Main {
  2. public static void main(String[] args) {
  3. Scanner cin = new Scanner(System.in);
  4. String input = cin.nextLine();
  5. double t = new ExpTree().solve(input);
  6. T.d(t);
  7. double s = new ExpStack().solve(input);
  8. T.d(s);
  9. }
  10. }

Java描述表达式求值的两种解法:双栈结构和二叉树的更多相关文章

  1. [Java]算术表达式求值之三(中序表达式转二叉树方案 支持小数)

    Entry类 这个类对表达式的合法性进行了粗筛: package com.hy; import java.io.BufferedReader; import java.io.IOException; ...

  2. 蓝桥杯算法训练 java算法 表达式求值

    问题描述 输入一个只包含加减乖除和括号的合法表达式,求表达式的值.其中除表示整除. 输入格式 输入一行,包含一个表达式. 输出格式 输出这个表达式的值. 样例输入 1-2+3*(4-5) 样例输出 - ...

  3. [Java]算术表达式求值之二(中序表达式转后序表达式方案,支持小数)

    Inlet类,入口类,这个类的主要用途是验证用户输入的算术表达式: package com.hy; import java.io.BufferedReader; import java.io.IOEx ...

  4. java实现表达式求值 (20 分)-------非递归版

    Dr.Kong设计的机器人卡多掌握了加减法运算以后,最近由学会了一些简单的函数求值.比如,它知道函数min(20, 23)的值是20, add(10, 98)的值是108等等.经过训练,Dr.Kong ...

  5. [Java]算术表达式求值之一(中序表达式转后序表达式方案)

    第二版请见:https://www.cnblogs.com/xiandedanteng/p/11451359.html 入口类,这个类的主要用途是粗筛用户输入的算术表达式: package com.h ...

  6. 《EOPL》: 实现了惰性求值的两种参数传递策略

    call-by-need 不过是比 call-by-name 多了一个 memorization 的步骤

  7. hdu 4192 (表达式求值)

    <题目链接> <转载于 >>>  > 题目大意: 给你n个数,和一个最终的结果,再给你一个含有n个不同变量的式子,问你这个式子最终能否得到指定的答案. 解题分 ...

  8. 利用栈实现算术表达式求值(Java语言描述)

    利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...

  9. java实现算术表达式求值

    需要根据配置的表达式(例如:5+12*(3+5)/7.0)计算出相应的结果,因此使用java中的栈利用后缀表达式的方式实现该工具类. 后缀表达式就是将操作符放在操作数的后面展示的方式,例如:3+2 后 ...

随机推荐

  1. C#编程.函数.参数

    详细内容请参见<C#入门经典(第4版)>p101页 1.参数匹配 在调用函数时,必须使参数与函数定义中指定的参数完全匹配,这意味着要匹配参数的类型.个数.和顺序. 注:函数签名由函数的名称 ...

  2. spark 源码分析之十--Spark RPC剖析之TransportResponseHandler、TransportRequestHandler和TransportChannelHandler剖析

    spark 源码分析之十--Spark RPC剖析之TransportResponseHandler.TransportRequestHandler和TransportChannelHandler剖析 ...

  3. shiro解析ini文件

    来吧,看看shiro是怎么解析ini文件的,这里假设ini文件在classpath下,名字叫做shiro.ini Factory<org.apache.shiro.mgt.SecurityMan ...

  4. spring解析配置文件(三)

    一.从XmlBeanDefinitionReader的registerBeanDefinitions(doc,resource)开始 protected int doLoadBeanDefinitio ...

  5. 10w数组去重,排序,找最多出现次数(精华)

    package cn.tedu.javaweb.test; import java.util.*; /* * @author XueWeiWei * @date 2019/6/11 8:19 */@S ...

  6. 走近OPENCV // opencv 2.4.9+vs2013配置

    一直很懒去配opencv,这几周忍不了终于抽空来配了一下环境... 用的是旧版opencv2.4系列,最新到3.0了,之后再看看教程不知道有什么特别大的区别. (FF14国服没有4.0 // 8.19 ...

  7. [开源] .NETCore websocket 即时通讯组件---ImCore

    前言 ImCore 是一款 .NETCore 下利用 WebSocket 实现的简易.高性能.集群即时通讯组件,支持点对点通讯.群聊通讯.上线下线事件消息等众多实用性功能. 开源地址:https:// ...

  8. 前端笔记之React(五)Redux深入浅出

    一.Redux整体感知 Redux是JavaScript状态管理容器,提供了可被预测状态的状态管理容器.来自于Flux思想,Facebook基于Flux思想,在2015年推出Redux库. 中文网站: ...

  9. RocketMQ中NameServer的启动

    在RocketMQ中,使用NamesrvStartup作为启动类 主函数作为其启动的入口: public static void main(String[] args) { main0(args); ...

  10. ByteBuf

    ByteBuf readerIndex ,读索引 writerIndex ,写索引 capacity ,当前容量 maxCapacity ,最大容量,当 writerIndex 写入超过 capaci ...