求一个包加减乘除和小括号的字符串的结果
解题思路和代码原型来源于牛课网, 我自己实现了一遍,添加了部分注释
1 package problems_2016_08_10; 2 3 import java.util.LinkedList; 4 /* 5 总述: 6 一共有五个函数分别是: 7 8 getvalue(): 9 参数:待求的字符串、 10 作用:一个启动函数,调用value函数 11 返回值:返回value函数返回值的第一个参数,就是求出的值 12 13 value(): 14 参数:总的待求的字符串,从哪一个位置开始求 15 作用:这个函数只是可以求不加小括号的情况。 16 在这种情况下,把数字和加减乘除放进que双端链表中,并且在放的时候用addnum方法,将乘除处理掉,只剩加减和数字,再将que链表通过getnum方法求出具体的值,返回给上一层。 17 因为不能处理小括号,所以碰见小括号就将括号里面的内容抛给下一层递归(value函数)。 18 这个value函数只有当发现右括号就停止 19 返回值:value函数在返回求出的结果的时候,还要返回处理的右括号的位置的索引值 20 addnum() 21 参数:que双端链表(这个是在value函数里面定义的),一个待添加进来的数字(value里面的pre) 22 作用:将字符串中紧挨的数字(表示是一个多位数字)转化为一个数字,并且将他加入到que中,在加入时处理掉que中的乘除号 23 返回值:没有返回值。因为是在value内部调用的,将当层递归的que变成了只包含加减和数字的字符 24 25 getnum() 26 参数:只包含加减和数字的que 27 作用:将只包含加减和数字的que求出结果 28 返回值:返回的是每一层小括号中(也是每一层递归当中)的结果 29 main() 30 用来进行检测代码功能的函数 31 32 33 */ 34 public class ExpressionCompute { 35 36 public static int getValue(String str) { 37 return value(str.toCharArray(), 0)[0]; 38 } 39 40 public static int[] value(char[] str, int i) { 41 LinkedList<String> que = new LinkedList<String>(); 42 int pre = 0; 43 int[] bra = null; 44 while (i < str.length && str[i] != ')') {//如果i(当前操作位置)还没有到达最有端,并且还没有碰到右边的括号,那就继续执行这个递归函数 45 if (str[i] >= '0' && str[i] <= '9') {//如果当前操作的是两个运算符号之间的数字 46 pre = pre * 10 + str[i++] - '0';//将字符串格式的数字转换为int,减'0'相当于减了这个字符的ascii码的值,字符串就变成数字了 47 } else if (str[i] != '(') {//如果发现也没有碰到了左括号,那说明碰到的是运算符 48 addNum(que, pre);//这个函数的功能是在pre加入que时看一下que的最后一个字符(肯定是运算符)是否为乘除,是的话进行结算 49 que.addLast(String.valueOf(str[i++]));//在上面获取到结算的结果加入之后下一位肯定是运算符,也加入 50 pre = 0;//把pre(用来存放两个运算符之间的数值的变量)清零 51 } else {//说明遇到左括号里 52 bra = value(str, i + 1);//碰到了左括号,那么就调用这个函数本身,将左括号之后的内容加入到内层递归中(传入的是左括号的下一个位置的索引) 53 pre = bra[0];//value函数返回一个数组(第29行) 第一个参数为处理过的值 54 i = bra[1] + 1;//第二个是处理到的值,+1表示下次开始处理的位置 55 } 56 } 57 addNum(que, pre);//最后没有遇到加减乘除 所以还要再来一次addnum 58 return new int[] { getNum(que), i };//返回这一层递归中的值和 处理到的位置i 59 } 60 61 public static void addNum(LinkedList<String> que, int num) {//这个函数的作用可以看成和que.addLast类似,que.addLast是直接把符号插入到que中需要用到的方法,当时当你想插入数字时需要看一下前一个符号的情况,所以自己封装了一个稍微复杂点儿的类似的方法 62 if (!que.isEmpty()) {//判断是否为空 63 int cur = 0; 64 String top = que.pollLast();//先弹出一个 65 if (top.equals("+") || top.equals("-")) { 66 que.addLast(top);//如果之前是加减,直接把这个数字放进去就可以了 67 } else {//如果之前的符号为乘除,那需要运算之后才能放入(保证que里面始终只放加减符号和数字) 68 cur = Integer.valueOf(que.pollLast());//再弹出一个 69 num = top.equals("*") ? (cur * num) : (cur / num);//将第二次弹出的(肯定是一个数字,由19 20行可知) 70 } 71 } 72 que.addLast(String.valueOf(num));//为空的话直接插入num,不为空让算出的结果等于num,好巧妙,又省了几行代码 73 } 74 75 public static int getNum(LinkedList<String> que) {//getnum的方法的作用仅仅是求一个只包含加减和数字的数组,的运算结果 76 int res = 0;//用来保存结果 77 boolean add = true;//用来描述一种状态,其实就是区分加减 78 String cur = null;//用来存放每次新的pollFirst()出来的值(是string类型的) 79 int num = 0;//用来保存cur转换回来的num类型 80 while (!que.isEmpty()) {//判断que里面是不是空的 81 cur = que.pollFirst();//弹出双向列表第一个 82 if (cur.equals("+")) {//如果弹出的是加号 83 add = true;//将add设置为true,用于下一个检测到数字的时候使用 84 } else if (cur.equals("-")) {//如果弹出的是减号 85 add = false;//将add设置为false,用于下一个检测到数字的时候使用 86 } else {//如果不为加减那么肯定就是数字了 87 num = Integer.valueOf(cur);//将字符类型的准换成数字类型 88 res += add ? num : (-num);//res+=(+num或者-num),三目运算符大大减少代码数量 89 } 90 } 91 return res;//返回最后求出的结果 92 } 93 94 public static void main(String[] args) {//进行一些测试 95 String exp = "48*((70-65)-43)+8*1"; 96 System.out.println(getValue(exp)); 97 98 exp = "4*(6+78)+53-9/2+45*8"; 99 System.out.println(getValue(exp)); 100 101 exp = "10-5*3"; 102 System.out.println(getValue(exp)); 103 104 exp = "-3*4"; 105 System.out.println(getValue(exp)); 106 107 exp = "3+1*4"; 108 System.out.println(getValue(exp)); 109 110 } 111 112 }