java根据Stack栈实现公式解析和自定义函数(一)
我是看到了大佬的博客还能这样解析,我们来解析一下思路并扩展一下,传送门: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); //刚寻找到一个运算符,并初始化一个参数个数为0
temp = "";
} 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 循环判断之前如果还是数值则相加,结果为18 i = 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 循环判断之前如果还是数值则相加,结果为18 i = 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); //刚寻找到一个运算符,并初始化一个参数个数为0
temp = "";
} 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 循环判断之前如果还是数值则相加,结果为18
i = 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 循环判断之前如果还是数值则相加,结果为18
i = 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栈实现公式解析和自定义函数(二)