堆棧(也簡稱做棧)是一種特殊的線性表,堆棧的數據元素以及數據元素間的邏輯關係和線性表徹底相同,其差異是線性表容許在任意位置進行插入和刪除操做,而堆棧只容許在固定一端進行插入和刪除操做。java
堆棧中容許進行插入和刪除操做的一端稱爲棧頂,另外一端稱爲棧底。堆棧的插入和刪除操做一般稱爲進棧或入棧,堆棧的刪除操做一般稱爲出棧或退棧。正則表達式
Java中已經出了Stack的具體實現類算法
堆棧的數據集合能夠表示爲a0,a1,…,an-1,每一個數據元素的數據類型能夠是任意的類類型。數組
操做集合數據結構
(1)入棧push(obj):把數據元素obj插入堆棧。ide
(2)出棧pop():出棧, 刪除的數據元素由函數返回。函數
(3)取棧頂數據元素getTop():取堆棧當前棧頂的數據元素並由函數返回。spa
(4)非空否notEmpty():若堆棧非空則函數返回true,不然函數返回false。設計
堆棧是各類軟件系統中應用最普遍的數據結構之一。括號匹配和表達式計算是編譯軟件中的基本問題,其軟件設計中都須要使用堆棧。code
首先來看應用之一:
中綴表達式轉爲後綴表達式
一、前綴表達式(Prefix Notation)是指將運算符寫在前面操做數寫在後面的不包含括號的表達式,並且爲了記念其發明者波蘭數學家Jan Lukasiewicz因此前綴表達式也 叫作「波蘭表達式」。好比- 1 + 2 3
二、後綴表達式(Postfix Notation)與之相反,是指運算符寫在操做數後面的不含括號的算術表達式,也叫作逆波蘭表達式。好比1 2 3 + -
不包含括號,運算符放在兩個運算對象的後面,全部的計算按運算符出現的順序,嚴格從左向右進行(再也不考慮運算符的優先規則,如:2 1 + 3 *
三、中綴表達式(Infix Notation)就是經常使用的將操做符放在操做數中間的算術表達式。前綴表達式和後綴表達式相對於中綴表達式最大的不一樣就是去掉了表示運算優先級 的括號,好比1-2+3
在中綴表達式的狀況下求值,既要考慮括號,優先級,還要考慮操做出現的前後順序。可是,做爲計算機,其計算過程就顯的比較複雜,對於一箇中綴表達式,須要不停地對錶達式進行屢次遍歷,來查找相應的計算的信息。這樣從算法複雜度上來講,是不可取的。前綴表達式和後綴表達式相對於人們經常使用的中綴表達式最大的不一樣就在於表達式中的運算符是按照必定的順序出現(接下來會具體講解),因此求值過程當中並不須要在表達式中使用括號來指定運算順序,也不須要在計算過程當中其中考慮運算符號的優先級。在採用輔助數據結構的狀況下,只須要對錶達式進行一次遍歷便可計算出結果,大大下降了算法複雜度,也更加符合傳統計算機的工做方式。
// 採用中綴表達式的算法分析
將中綴表達式轉換爲後綴表達式:eg:
(1)當讀到數字直接送至輸出隊列中;
(2)當讀到運算符t時:
a.將棧中全部優先級高於或等於t的運算符彈出,送到輸出隊列中;
這句話很差理解,能夠說成這樣,從棧頂開始,依次彈出比當前處理的運算符優先級高的運算符,直到一個比它優先級低的或者遇到了一個左括號就中止。
b.t進棧;
(3)讀到左括號時老是將它壓入棧中;
(4)讀到右括號時,將靠近棧頂的第一個左括號上面的運算符所有依次彈出,送至輸出隊列後,再丟棄左括號;
(5)中綴表達式所有讀完後,若棧中仍有運算符,將其送到輸出隊列中。
中綴表達式:3+(2-5)*6/3 轉換爲後綴表達式的過程:
後綴表達式 棧
3
3 +
3 +(
32 +(
32 +(-
325 +(-
325- +
325- +*
325-6 +*
325-6* +/
325-6*3 +/
325-6*3/+
最終後綴表達式爲:325-6*3/+
運用後綴表達式進行計算:
(1)創建一個棧S;
(2)從左到右讀後綴表達式,讀到數字就將它轉換爲數值壓入棧S中,讀到運算符則從棧中依次彈出兩個數分別到Y和X,而後以「X 運算符 Y」的形式計算機出結果,再壓加棧S中;
(3)若是後綴表達式未讀完,就重複上面過程,最後輸出棧頂的數值則爲結束。
3+(2-5)*6/3=-3 ,其後綴表達式爲:325-6*3/+
運算過程以下:
棧 運算
3 2 5 325入棧
3 2-5=-3
3 -3 運算結果進棧
3 -3 6
3 -3*6=-18
3 -18 3 -18/3=-6
3 -6 3+(-6)=-3
-3
//------------------------------------------------------
//直接分析:代碼解析都給在了註釋裏面
1 import java.util.Stack; 2 import java.util.regex.Pattern; 3 4 /** 5 * 將中綴表達式字符串轉換爲後綴表達式 6 */ 7 public class StringToArithmetic { 8 // 默認構造 9 public StringToArithmetic() { 10 11 } 12 13 // 將中綴表達式字符串計算獲得結果 14 public static double stringToArithmetic(String string) { 15 return suffixToArithmetic(infixToSuffix(string)); 16 } 17 18 // 將中綴表達式轉換爲後綴表達式 19 public static String infixToSuffix(String exp) { 20 // 建立操做符堆棧 21 Stack<Character> s = new Stack<Character>(); 22 // 要輸出的後綴表達式字符串 23 String suffix = ""; 24 int length = exp.length(); // 輸入的中綴表達式的長度 25 for (int i = 0; i < length; i++) { 26 char temp;// 臨時字符變量 27 // 獲取該中綴表達式的每個字符並進行判斷 28 char ch = exp.charAt(i); 29 switch (ch) { 30 // 忽略空格 31 case ' ': 32 break; 33 // 若是是左括號直接壓入堆棧 34 case '(': 35 s.push(ch); 36 break; 37 38 // 碰到'+' '-',將棧中的全部運算符所有彈出去,直至碰到左括號爲止,輸出到隊列中去 39 case '+': 40 case '-': 41 while (s.size() != 0) { 42 temp = s.pop(); 43 if (temp == '(') { 44 // 從新將左括號放回堆棧,終止循環 45 s.push('('); 46 break; 47 } 48 suffix += temp; 49 } 50 // 沒有進入循環說明是當前爲第一次進入或者其餘前面運算都有括號等狀況致使棧已經爲空,此時須要將符號進棧 51 s.push(ch); 52 break; 53 54 // 若是是乘號或者除號,則彈出全部序列,直到碰到加好、減號、左括號爲止,最後將該操做符壓入堆棧 55 case '*': 56 case '/': 57 while (s.size() != 0) { 58 temp = s.pop(); 59 // 只有比當前優先級高的或者相等的纔會彈出到輸出隊列,遇到加減左括號,直接中止當前循環 60 if (temp == '+' || temp == '-' || temp == '(') { 61 s.push(temp); 62 break; 63 } else { 64 suffix += temp; 65 } 66 } 67 // 沒有進入循環說明是當前爲第一次進入或者其餘前面運算都有括號等狀況致使棧已經爲空,此時須要將符號進棧 68 s.push(ch); 69 break; 70 71 // 若是碰到的是右括號,則距離棧頂的第一個左括號上面的全部運算符彈出棧並拋棄左括號 72 case ')': 73 // 這裏假設必定會遇到左括號了,此爲本身改進版,已經驗證能夠過 74 // while ((temp = s.pop()) != '(') { 75 // suffix += temp; 76 // } 77 while (!s.isEmpty()) { 78 temp = s.pop(); 79 if (temp == '(') { 80 break; 81 } else { 82 suffix += temp; 83 } 84 } 85 break; 86 // 默認狀況,若是讀取到的是數字,則直接送至輸出序列 87 default: 88 suffix += ch; 89 break; 90 } 91 92 } 93 // 若是堆棧不爲空,則把剩餘運算符一次彈出,送至輸出序列 94 while (s.size() != 0) { 95 suffix += s.pop(); 96 } 97 // 98 return suffix; 99 } 100 101 // 將後綴表達式的進行計算獲得運算結果 eg:325-6*3/+ 102 public static double suffixToArithmetic(String exp) { 103 // 使用正則表達式匹配數字 104 Pattern pattern = Pattern.compile("\\d+||(\\d+\\.\\d+)"); 105 // 將後綴表達式分割成字符串數組,此處直接使用空白也能夠對字符串進行分割!! 106 String[] strings = exp.split(""); 107 Stack<Double> stack = new Stack<Double>(); 108 for (int i = 0; i < strings.length; i++) { 109 // 這裏最好是進行判斷完全消除空格,在該數組的第一位爲一個隱形的空格,這裏必定要注意在使用exp.split("")剔除空白"" 110 // 因爲使用的是""截取致使在數組第一位上面的值爲空白 111 if (strings[i].equals("")) { 112 continue; 113 } 114 // 若是遇到了數字則直接進棧 115 if (pattern.matcher(strings[i]).matches()) { 116 stack.push(Double.parseDouble(strings[i])); 117 } 118 // 若是是運算符,則彈出棧頂的兩個數進行計算 119 else { 120 // !!!這裏須要注意,先彈出的那個數實際上是第二個計算數值,這裏記做y! 121 // 本身書寫的時候出錯 122 double y = stack.pop(); 123 double x = stack.pop(); 124 // 將運算結果從新壓棧 125 stack.push(calculate(x, y, strings[i])); 126 } 127 } 128 // 彈出棧頂元素就是最終結果 129 return stack.pop(); 130 } 131 132 private static Double calculate(double x, double y, String string) { 133 // TODO Auto-generated method stub 134 // 其實使用case邏輯也能夠 135 if (string.trim().equals("+")) { 136 return x + y; 137 } 138 if (string.trim().equals("-")) { 139 return x - y; 140 } 141 if (string.trim().equals("*")) { 142 return x * y; 143 } 144 if (string.trim().equals("/")) { 145 return x / y; 146 } 147 return (double) 0; 148 } 149 150 }
1 import java.io.IOException; 2 import java.util.Stack; 3 4 /* 5 * 使用jdk自帶stack進行模擬, 6 * 中綴表達式轉爲後綴表達式 7 */ 8 public class Test { 9 public static void main(String[] args) throws Exception { 10 // Stack<Character> s = new Stack<Character>(); 11 // 12 // char ch; 13 // // 鍵盤錄入一行數據,#號結束錄入 14 // // 而後壓棧 15 // while ((ch = (char) (System.in.read())) != '#') { 16 // s.push(ch); 17 // } 18 // 19 // // 彈出堆棧 20 // while (!s.isEmpty()) { 21 // System.out.println(s.pop()); 22 // } 23 24 String str = "3+(2-5)*6/3"; // 後綴表達式爲: 325-6*3/+ 25 String str2 = "3+2-5"; 26 27 System.out.println(StringToArithmetic.infixToSuffix(str2)); 28 System.out.println(StringToArithmetic.stringToArithmetic(str2)); 29 30 // String[] arr = StringToArithmetic.infixToSuffix(str).split(""); 31 // for (int i = 0; i < arr.length; i++) { 32 // if (arr[i].equals("")) { 33 // System.out.println(arr[i]); 34 // System.out.println("我靠 " + i); 35 // } 36 // } 37 // 38 // System.out.println(arr[0]); 39 } 40 }