我是看到了大佬的博客还能这样解析,我们来解析一下思路并扩展一下,传送门:java实现公式解析

1. Stack的介绍

栈(stack)在计算机科学中是限定仅在表尾进行插入或删除操作的线性表。栈是一种数据结构,它按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据。栈是只能在某一端插入和删除的特殊线性表。用桶堆积物品,先堆进来的压在底下,随后一件一件往上堆。取走时,只能从上面一件一件取。读和取都在顶部进行,底部一般是不动的。栈就是一种类似桶堆积物品的数据结构,进行删除和插入的一端称栈顶,另一端称栈底。插入一般称为进栈,删除则称为退栈。 栈也称为后进先出表。

我们简单了解了一下Stack栈,虚拟机里的核心构成栈就占了2/5,像我们遇到的 StackOverflowError 和 OutOfMemory 问题就是栈出现了问题,想了解一下的大佬们可以看我最开始的博客:简要了解JVM

2. Java解析公式的思路

假如我们实现(1+1,1-1,1*1,1/1)这些简单功能的时候我们可以直接用程序的符号就可以让计算机识别出来,但是在处理函数时或者运算的时候可能并不会那么简单实现,那我们来自己实现一个支持普通运算,聚合函数,条件判断的功能。

先来看一下源代码

package com.liu.ex2;import java.util.Scanner;
import java.util.Stack;public class Main {public static Stack<String> operation = new Stack<String>();  //存放运算符public static Stack<Character> bracket = new Stack<Character>(); //存放左括号public static Stack<Integer> number = new Stack<Integer>(); //存放运算参数public static Stack<Integer> count = new Stack<Integer>(); //存放运算符参数个数public int add(int[] N) {if(N.length == 3)return N[0] + N[1] + N[2];return N[0] + N[1];}public int max(int[] N) {return N[0] > N[1] ? N[0] : N[1];}public int min(int[] N) {return N[0] < N[1] ? N[0] : N[1];}public int doubleMe(int[] N) {return 2 * N[0];}public boolean judgeChar(char s) {if(s >= 'a' && s <= 'z' || s >= 'A' && s <= 'Z')return true;return false;}public boolean judgeNumber(char s) {if(s >= '0' && s <= '9')return true;return false;}public void getResult(String A) {String temp = "";for(int i = 0;i < A.length();i++) {if(judgeChar(A.charAt(i))) {temp = temp + A.charAt(i);i = i + 1;while(judgeChar(A.charAt(i))) {temp = temp + A.charAt(i);i++;}i = i - 1;operation.push(temp);count.push(0);  //刚寻找到一个运算符,并初始化一个参数个数为0temp = "";} else if(A.charAt(i) == '(') {bracket.push(A.charAt(i));} else if(judgeNumber(A.charAt(i))) {temp = temp + A.charAt(i);i = i + 1;while(judgeNumber(A.charAt(i))) {temp = temp + A.charAt(i);i++;}i = i - 1;number.push(Integer.valueOf(temp));count.push(count.pop() + 1);    //此处用于计算当前栈顶运算符实际参数个数temp = "";} else if(A.charAt(i) == ')') {  //此时要进行运算bracket.pop();  //栈顶左括号出栈String tempOpera = operation.pop();int[] N = new int[count.pop()];if(!count.empty())count.push(count.pop() + 1);for(int j = 0;j < N.length;j++)N[j] = number.pop();int result = 0;if(tempOpera.equals("add"))result = add(N);else if(tempOpera.equals("max"))result = max(N);else if(tempOpera.equals("min"))result = min(N);else if(tempOpera.equals("doubleMe"))result = doubleMe(N);number.push(result);}}}public static void main(String[] args) {Main test = new Main();Scanner in = new Scanner(System.in);String A = in.nextLine();test.getResult(A);System.out.println(number.pop());}
}

梳理一下大佬的实现思路:

a. 循环传递的公式字符串,按字符解析

b. 判断字符:如果是字母类型则push到运算符的栈里。

判断左括号:一个嵌套的开始,push到左括号的栈里。

判断数值:验证是否数值类型并push到参数也就是值的栈里

判断右括号:右括号是唯一没有定义栈的,因为到这里就是一个结束需要计算,只要拿到结果就可以

c. 出栈,在右括号这一步就要执行计算得到结果并要将这一层的括号及参数和公式进行出栈,得到的结果再入栈到参数栈以进行      下一步计算

3. 问题

a. 上面的源代码要求运算符必须在前也就是第一个验证的是字符串时才初始化参数:count.push(0); 

b. 一个函数里多个参数输出结果不正确,因为源码只比对了两个值,源码:return N[0] < N[1] ? N[0] : N[1];

c. 如果使用+ - * / 输出的结果不正确,因为源代码没有对+ - * / 做处理。

d. 如果我们在前面或者后面加一个数值加减乘除就会报错,因为上面初始化的问题

4. 拓展解决

a.

if (count.size()>0){count.push(count.pop() + 1);    //此处用于计算当前栈顶运算符实际参数个数
}else{count.push(0);
}

在参数值执行出栈的时候验证一下栈大小,如果不大于0则初始化

b. 一个函数里多个参数输出结果不正确,因为源码只比对了两个值,源码:return N[0] < N[1] ? N[0] : N[1];

public int min(int[] N) {int i = 0;for (int f = 0; f < N.length; f++){if (f == 0){i = N[f];}else{if (i>N[f]){i = N[f];}}}return i;}

循环处理所有参数

c. 如果使用+ - * / 输出的结果不正确,因为源代码没有对+ - * / 做处理。

d. 如果我们在前面或者后面加一个数值加减乘除就会报错,因为上面初始化的问题

心态要炸了,每次写道这Ctrl+z都给我撤回到上面的进度,我太难了。

不多说了,一次比一次少,直接上源代码,问题就是还不能支持加减乘除括号和函数,因为他们还不识别。

支持的公式:max(add(min(1,2,4),max(2,18-1,20-1)),22)

不支持的公式:1+max(1,2,3) or 1+(1+1)

上面的问题是以后要优化的,这一部分的源码如下,所有代码在下面也会发出,怕找着麻烦,这一部分单独拿出来

if(A.charAt(i) == '+' || A.charAt(i) == '-' || A.charAt(i) == '*' || A.charAt(i) == '/'){// 先将i-1为获取前一个值,当前是计算符号i = i-1;// 初始化前值String sendTemp = String.valueOf(A.charAt(i));// i-1往前循环判断如果有数值类型则字符串相加,比如18+1 检索到前一个字符为8 循环判断之前如果还是数值则相加,结果为18i = i-1;while (judgeNumber(A.charAt(i))){sendTemp = A.charAt(i) + sendTemp;i--;}// 循环完毕i恢复原始位置i = i + 3;String a = sendTemp;// 先将i+1为获取后一个值,当前是计算符号i = i+1;// 初始化后值String temps = String.valueOf(A.charAt(i));// i+1往后循环判断如果有数值类型则字符串相加,比如1+28 检索到第一个字符为2 循环判断之前如果还是数值则相加,结果为18i = i+1;while (judgeNumber(A.charAt(i))){temps = A.charAt(i) + temps;i++;}// 循环完毕i恢复原始位置i = i - 2;String b = temps;// 得到值,求结果int c = 0;if (A.charAt(i) == '+'){c = Integer.valueOf(a)+Integer.valueOf(b);}if (A.charAt(i) == '-'){c = Integer.valueOf(a)-Integer.valueOf(b);}if (A.charAt(i) == '*'){c = Integer.valueOf(a)*Integer.valueOf(b);}if (A.charAt(i) == '/'){c = Integer.valueOf(a)/Integer.valueOf(b);}// 先摘出加号之前的值,我们要存的是总值number.pop();// push结果数据,count栈不增加,因为在加号之前已经加过一次参数值当作此次的个数number.push(c);i = i + 1;
}

1.0 版本所有代码-------------------------------------------------------------------------------------------------------------------------

package com.example.demo.java;import java.time.Month;
import java.util.Scanner;
import java.util.Stack;/*** @author Anzepeng* @title: JavaCalculate* @projectName demo* @description: TODO* @date 2020/6/4 0004上午 9:06*/
public class JavaCalculate {public static Stack<String> operation = new Stack<String>();  //存放运算符public static Stack<Character> bracket = new Stack<Character>(); //存放左括号public static Stack<Integer> number = new Stack<Integer>(); //存放运算参数public static Stack<Integer> count = new Stack<Integer>(); //存放运算符参数个数public int add(int[] N) {int i = 0;for (int f = 0; f < N.length; f++){i += N[f];}return i;}public int max(int[] N) {int i = 0;for (int f = 0; f < N.length; f++){if (f == 0){i = N[f];}else{if (i<N[f]){i = N[f];}}}return i;}public int min(int[] N) {int i = 0;for (int f = 0; f < N.length; f++){if (f == 0){i = N[f];}else{if (i>N[f]){i = N[f];}}}return i;}public int doubleMe(int[] N) {return 2 * N[0];}public boolean judgeChar(char s) {if(s >= 'a' && s <= 'z' || s >= 'A' && s <= 'Z')return true;return false;}public boolean judgeNumber(char s) {if(s >= '0' && s <= '9')return true;return false;}public void getResult(String A) {String temp = "";for(int i = 0;i < A.length();i++) {// 验证是否在a和z之间if(judgeChar(A.charAt(i))) {temp = temp + A.charAt(i);i = i + 1;while(judgeChar(A.charAt(i))) {temp = temp + A.charAt(i);i++;}i = i - 1;operation.push(temp);count.push(0);  //刚寻找到一个运算符,并初始化一个参数个数为0temp = "";} else if(A.charAt(i) == '(') {bracket.push(A.charAt(i));} else if(judgeNumber(A.charAt(i))) {temp = temp + A.charAt(i);i = i + 1;while (judgeNumber(A.charAt(i))) {temp = temp + A.charAt(i);i++;}i = i - 1;number.push(Integer.valueOf(temp));if (count.size() > 0) {count.push(count.pop() + 1);    //此处用于计算当前栈顶运算符实际参数个数} else {count.push(0 + 1);}temp = "";} else if(A.charAt(i) == '+' || A.charAt(i) == '-' || A.charAt(i) == '*' || A.charAt(i) == '/'){// 先将i-1为获取前一个值,当前是计算符号i = i-1;// 初始化前值String sendTemp = String.valueOf(A.charAt(i));// i-1往前循环判断如果有数值类型则字符串相加,比如18+1 检索到前一个字符为8 循环判断之前如果还是数值则相加,结果为18i = i-1;while (judgeNumber(A.charAt(i))){sendTemp = A.charAt(i) + sendTemp;i--;}// 循环完毕i恢复原始位置i = i + 3;String a = sendTemp;// 先将i+1为获取后一个值,当前是计算符号i = i+1;// 初始化后值String temps = String.valueOf(A.charAt(i));// i+1往后循环判断如果有数值类型则字符串相加,比如1+28 检索到第一个字符为2 循环判断之前如果还是数值则相加,结果为18i = i+1;while (judgeNumber(A.charAt(i))){temps = A.charAt(i) + temps;i++;}// 循环完毕i恢复原始位置i = i - 2;String b = temps;// 得到值,求结果int c = 0;if (A.charAt(i) == '+'){c = Integer.valueOf(a)+Integer.valueOf(b);}if (A.charAt(i) == '-'){c = Integer.valueOf(a)-Integer.valueOf(b);}if (A.charAt(i) == '*'){c = Integer.valueOf(a)*Integer.valueOf(b);}if (A.charAt(i) == '/'){c = Integer.valueOf(a)/Integer.valueOf(b);}// 先摘出加号之前的值,我们要存的是总值number.pop();// push结果数据,count栈不增加,因为在加号之前已经加过一次参数值当作此次的个数number.push(c);i = i + 1;} else if(A.charAt(i) == ')') {  //此时要进行运算bracket.pop();  //栈顶左括号出栈String tempOpera = operation.pop();int[] N = new int[count.pop()];if(!count.empty())count.push(count.pop() + 1);for(int j = 0;j < N.length;j++){N[j] = number.pop();}int result = 0;if(tempOpera.equals("add"))result = add(N);else if(tempOpera.equals("max"))result = max(N);else if(tempOpera.equals("min"))result = min(N);else if(tempOpera.equals("doubleMe"))result = doubleMe(N);number.push(result);}}}public static void main(String[] args) {formula();}public static void formula(){JavaCalculate test = new JavaCalculate();test.getResult("max(add(min(1,2,4),max(2,18-1,20-1)),22)");System.out.println("函数多参数:"+number.pop());/*test.getResult("add(min(2,4),max(2,8))");System.out.println("规定格式:"+number.pop());test.getResult("add(min(1,2,4),max(2,8,12))");System.out.println("函数多参数:"+number.pop());test.getResult("add(min(5,3-1),max(2,8))");System.out.println("错误结果:"+number.pop());test.getResult("1+add(min(5,3),max(2,8))");System.out.println("前缀+:"+number.pop());*/}public static void speed(){JavaCalculate test = new JavaCalculate();long sTime = System.currentTimeMillis();for (int i = 0; i < 1000000; i++){test.getResult("max(add(min(1,2,4),max(2,18-1,20-1)),22)");}long eTime = System.currentTimeMillis();System.out.println("耗时+:"+(eTime-sTime));}
}

第二篇:java根据Stack栈实现公式解析和自定义函数(二)

       

java根据Stack栈实现公式解析和自定义函数(一)相关推荐

  1. 如何给女朋友讲明白:Java中Stack(栈)与Heap(堆)

    背景 Java中Stack(栈)与Heap(堆)是面试中被经常问到的一个话题. 有没有对Java中Stack(栈)与Heap(堆)烂熟于心的童鞋,请举手!!!(怎么没人举手-) 这个时候蜗牛哥的对象弱 ...

  2. Java集合Stack源码深入解析

    概要 学完Vector了之后,接下来我们开始学习Stack.Stack很简单,它继承于Vector.学习方式还是和之前一样,先对Stack有个整体认识,然后再学习它的源码:最后再通过实例来学会使用它. ...

  3. java集合类——Stack栈类与Queue队列

    今日走读代码时,遇到stack栈类,特查看java的API文档,总结如下: Stack继承Vector类,它通过五个操作对类 Vector 进行了扩展. 栈是 后进先出的. 栈提供了通常的 push ...

  4. Java中Stack栈的方法

    栈的特点就是先进后出, 后进先出,在Stack类中有五种方法 返回类型 方法名 描述 E push(E item) 将传入的值入栈的顶部 E pop() 返回栈顶的值,并删除栈顶的对象 E peek( ...

  5. java什么是栈和堆_JAVA中的栈和堆

    JAVA在程序运行时,在内存中划分5片空间进行数据的存储.分别是:1:寄存器.2:本地方法区.3:方法区.4:栈.5:堆. 基本,栈stack和堆heap这两个概念很重要,不了解清楚,后面就不用学了. ...

  6. java内存stack heap_java内存解析-------stack(栈)和heap(堆)的理解

    学习编程的时候,经常会看到stack这个词,它的中文名字叫做"栈". 理解这个概念,对于理解程序的运行至关重要.容易混淆的是,这个词其实有三种含义,适用于不同的场合,必须加以区分. ...

  7. java实现顺序栈_Java实现顺序栈原理解析

    这篇文章主要介绍了java实现顺序栈原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 什么是栈 1.栈的英文是stack 2.栈是一个先入后出 ...

  8. java 线程栈大小配置,JVM运行时数据区详解-Stack栈(优化配置、代码样例)

    最近有段时间没有更新Netty的教程了,却发了一些其他的东西.可能有的朋友会问,难道这就完事了?不会的.两方面原因.第一.笔者也是需要工作的人,自然要完成好工作中的任务,这里面也有很多东西需要学习和研 ...

  9. Spads 公式解析系统 - Java

    2019独角兽企业重金招聘Python工程师标准>>> 很多网络应用中,涉及到一些内部运算的具体求值逻辑并不能够在开发阶段就定下来.这些逻辑需要随着使用,探测用户反应,不断修正.符合 ...

最新文章

  1. 2016年第七届蓝桥杯决赛Java本科B组试题解析
  2. 拉杰尔安卓服务器注册上限,拉结尔多开养小号刷副本 用多多云手机离线能升级...
  3. Win32 串口编程(一)
  4. 01背包+概率问题 计蒜客 offer
  5. 就要有鹤立鸡群的HTML5资本
  6. go 链路追踪_使用opentracing,jaeger实现golang链路追踪
  7. Morph-UGATIT:一种支持渐进式域迁移的图像翻译方法
  8. 程序员的进阶课-架构师之路(14)-B+树、B*树
  9. 【UML关系(泛化、实现、依赖、关联(聚合,组合))】
  10. Gbase与oracle数据库的区别
  11. WDS+MDT全自动部署系统、自动加域、自动计算机取名(SN序列号或MAC地址)
  12. smartpdf双击无法返回latex如何解决
  13. VOIP电话中的SIP信令返回码的详细解释
  14. Paper reading:A simple baseline for one-shot multi-object tracking(二)
  15. 计算机系统分别由,计算机系统由两部分组成它们分别是什么
  16. 物料移动类型和后勤自动科目设置-转
  17. ReactNative基础(六)使用react-navigation实现页面导航布局效果(TabNavigator)
  18. 2021 年最佳 3D 渲染 GPU
  19. CAD绘制区域覆盖对象
  20. 16 岁少年前往腾讯总部要求解封 QQ 账号;微软发内部信辟谣:没有关闭微软苏州的相关计划;Go 1.20 发布|极客头条

热门文章

  1. 电脑回收站清空的文件怎么恢复呢?
  2. MVC HTML页面使用
  3. 东北大学c语言绩点成绩,东北大学学生平均学分绩点(GPA)的相关规定
  4. pixy php,Pixy快速入门指南.pdf
  5. 互联网女皇解读2018大势:新技术、云服务和数据隐私【附294页中文版PPT】
  6. 电脑上如何实现微信双开或者多开
  7. watch、computed、methods的区别
  8. 大众、特斯拉、麦当劳、英特尔、爱奇艺、瑞幸等公司高管变动
  9. 惠普暗影精灵2 win10+linux双系统开机直接进win10,无法进系统选择界面的解决办法
  10. Android EditText java 设置 maxLength