問題:
給定字符串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 }
上面的步驟,並沒有實現帶有()、[]、{}的復雜運算,還有待后續研究.