- 1、堆棧-Stack
堆棧(也簡稱作棧)是一種特殊的線性表,堆棧的數據元素以及數據元素間的邏輯關系和線性表完全相同,其差別是線性表允許在任意位置進行插入和刪除操作,而堆棧只允許在固定一端進行插入和刪除操作。
堆棧中允許進行插入和刪除操作的一端稱為棧頂,另一端稱為棧底。堆棧的插入和刪除操作通常稱為進棧或入棧,堆棧的刪除操作通常稱為出棧或退棧。
Java中已經出了Stack的具體實現類
堆棧的數據集合可以表示為a0,a1,…,an-1,每個數據元素的數據類型可以是任意的類類型。
操作集合
(1)入棧push(obj):把數據元素obj插入堆棧。
(2)出棧pop():出棧, 刪除的數據元素由函數返回。
(3)取棧頂數據元素getTop():取堆棧當前棧頂的數據元素並由函數返回。
(4)非空否notEmpty():若堆棧非空則函數返回true,否則函數返回false。
堆棧是各種軟件系統中應用最廣泛的數據結構之一。括號匹配和表達式計算是編譯軟件中的基本問題,其軟件設計中都需要使用堆棧。
首先來看應用之一:
中綴表達式轉為后綴表達式
1、前綴表達式(Prefix Notation)是指將運算符寫在前面操作數寫在后面的不包含括號的表達式,而且為了紀念其發明者波蘭數學家Jan Lukasiewicz所以前綴表達式也 叫做“波蘭表達式”。比如- 1 + 2 3
2、后綴表達式(Postfix Notation)與之相反,是指運算符寫在操作數后面的不含括號的算術表達式,也叫做逆波蘭表達式。比如1 2 3 + -
不包含括號,運算符放在兩個運算對象的后面,所有的計算按運算符出現的順序,嚴格從左向右進行(不再考慮運算符的優先規則,如:2 1 + 3 *
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 }