使用堆栈结构进行字符串表达式("7*2-5*3-3+6/3")的计算
问题:
给定字符串String str = "7*2-5*3-3+6/3", 求出字符串里面表达式的结果?
像javascript有自带的eval()方法,可以直接计算。但java或其它语言,API并没有提供直接计算的方法(可能是我孤陋寡闻),这时就需要我们自己写方法实现了。
栈:
要实现上述功能,需要用到堆栈的概念,
栈是一种特殊的串行形式的数据结构,它的特殊在于,只能在队列的一端进行输入和输出,因此按照"先入后出"的原理运行。就像一个盒子,先放进的东西在下面,后面放进的东西反而在上面。如上图,栈可以看成是一个"倒立的数组"。有一个top索引,永远指向最上面一个节点。当加入一个元素时,top往上移一位。取走一个元素时,top往下移一位。当栈为空时,top指向栈的下方,top=-1.
根据上面的结构,我们可以自定义一个栈,并实现入栈(push)和弹栈(pop)两个方法:
1.入栈:将数据放入堆栈的顶端,top索引+1.
2.弹栈:将栈顶数据输出,top索引-1.
1 /** 2 * 使用数组模拟栈 3 * @author SCOTT 4 * 5 */ 6 public class MyStack { 7 8 private int top = -1; //指向栈顶的索引 9 private int maxSize = 100; //栈所能存储的最大个数 10 private Object[] array = new Object[maxSize]; //数组 11 12 /** 13 * 加入一个元素 14 * @param val 15 */ 16 public void push(Object val){ 17 if(top == maxSize - 1){ 18 System.out.println("栈满"); 19 return; 20 } 21 array[++top] = val; 22 } 23 24 /** 25 * 弹出一个元素 26 * @return 27 */ 28 public Object pop(){ 29 if(top == -1){ 30 System.out.println("栈空"); 31 return null; 32 } 33 return array[top--]; 34 } 35 36 /** 37 * 遍历 38 */ 39 public void toList(){ 40 for(int i=top;i>-1;i--){ 41 System.out.println(i+"="+array[i]); 42 } 43 } 44 45 /** 46 * 判断栈是否为空 47 * @return 48 */ 49 public boolean isEmpty(){ 50 if(top == -1) 51 return true; 52 return false; 53 } 54 55 /** 56 * 获取栈顶的元素,跟pop()方法的区别在于:pop()方法的top索引会减1,而getTop不会 57 */ 58 public Object getTop(){ 59 if(top == -1) 60 return null; 61 return array[top]; 62 } 63 }
对栈有了了解之后,就可以计算:String str = "7*2-5*3-3+6/3".
思路:
1.初始化两个栈,一个数栈(存放表达式中的数字),一个符号栈(用来存储表达式中的符号).
2.遍历字符串str,将字符一个个取出进行判断,假定用ch存储取出的字符.
2.1.如果ch为数字,直接将数字放入数栈(这是一种抽象的说法,如果存在像700这种两位以上的数字,需要自己拼接,然后再一起入栈,只占一个位置).
2.2.如果ch为符号.
2.2.1.如果符号栈为空,就直接将符号放入符号栈.
2.2.2.如果符号栈不为空, 就判断当前符号(ch)的运算级别跟符号栈栈顶的符号(放在符号栈最上面的一个运算符)运算级别谁大谁小?
2.2.2.1 循环判断,如果当前符号(ch)的优先级<=符号栈栈顶运算符的优先级,则进行计算。计算方式:从数栈中弹出两个数,和符号栈弹出一个符号,进行计算并得到结果res,将res再放入数栈。直到当前运算符的优先级>符号栈栈顶运算符的优先级为止。
2.2.2.2 将当前符号(ch)加入符号栈.
3.到此,已经将字符串str遍历完毕。然后再将数栈和符号栈的数据取出进行计算。计算的方式:先从数栈取出两个数,再从符号栈弹出符号,计算并得到结果res,然后将res放入数栈。依次循环,直到符号栈为空为止。
4.计算完毕后,在数栈会存在一个唯一的值,这个值就是我们需要的结果。
具体实现:
1 /** 2 * 计算字符串表达式 3 * @author SCOTT 4 * 5 */ 6 public class StackApp { 7 8 public static void main(String[] args) { 9 10 String str = "7*2-5*3-3+6/3"; 11 //str的索引,默认指向第一位 12 int index = 0; 13 //用于拼接连续数字 14 String tempStr = ""; 15 //数栈 16 MyStack numStatck = new MyStack(); 17 //符号栈 18 MyStack operStatck = new MyStack(); 19 20 while(true){ 21 char ch = str.charAt(index); 22 if(isNum(ch)){ 23 //先进行拼接,满足条件,在放入数栈 24 tempStr += ch; 25 //如果到了字符串末尾,或者下一位是符号,则放入数栈 26 if(index == str.length() - 1 || !isNum(str.charAt(index+1))){ 27 numStatck.push(tempStr); 28 } 29 }else{ 30 tempStr = ""; 31 if(operStatck.isEmpty()){ 32 //如果符号栈为空,则直接将运算符放入符号栈 33 operStatck.push(ch); 34 }else{ 35 //循环判断:如果当前运算符的级别<=栈顶运算符的级别,则计算 36 while(!operStatck.isEmpty() && getLevel(ch+"") <= getLevel(operStatck.getTop().toString())){ 37 int res = calculate(numStatck, operStatck); 38 //将res入数栈 39 numStatck.push(res); 40 } 41 //将符号放入符号栈 42 operStatck.push(ch); 43 } 44 } 45 46 index++; 47 //判断条件,break 48 if(index == str.length()) 49 break; 50 } 51 52 //遍历完成后,进行计算 53 while(!operStatck.isEmpty()){ 54 int res = calculate(numStatck, operStatck); 55 //将运算结果放入数栈 56 numStatck.push(res); 57 } 58 59 //最后在数栈肯定会存在唯一的结果 60 System.out.println(numStatck.pop());//-2 61 62 } 63 64 /** 65 * 判断截取位,是否是数字 66 * @param ch 67 * @return 68 */ 69 public static boolean isNum(char ch){ 70 if(ch == '+' || ch == '-' || ch == '*' || ch == '/') 71 return false; 72 return true; 73 } 74 75 /** 76 * *和/级别为1,+和-级别为0 77 * @return 78 */ 79 public static int getLevel(String oper){ 80 if("*".equals(oper) || "/".equals(oper)) 81 return 1; 82 return 0; 83 } 84 85 /** 86 * 计算结果://从数栈中取出两位,从符号栈中取出一位 87 * @param numStatck 88 * @param operStatck 89 * @return 90 */ 91 public static int calculate(MyStack numStatck, MyStack operStatck){ 92 int num1 = 0; 93 if(numStatck.getTop() != null) 94 num1 = Integer.parseInt(numStatck.pop().toString()); 95 int num2 = 0; 96 if(numStatck.getTop() != null){ 97 num2 = Integer.parseInt(numStatck.pop().toString()); 98 } 99 String str = ""; 100 if(operStatck.getTop() != null){ 101 str = operStatck.pop().toString(); 102 } 103 104 int res = 0; 105 switch (str.charAt(0)) { 106 case '+': 107 res = num1 + num2; 108 break; 109 case '-': 110 res = num2 - num1;//注意顺序,栈是先入后出的结构 111 break; 112 case '*': 113 res = num1 * num2; 114 break; 115 case '/': 116 res = num2 / num1;//注意顺序,栈是先入后出的结构 117 break; 118 default: 119 throw new RuntimeException("运算符不正确"); 120 } 121 122 return res; 123 } 124 }
上面的步骤,并没有实现带有()、[]、{}的复杂运算,还有待后续研究.