文章目錄
前綴表達式(波蘭表達式)
前綴表達式分析與介紹
- 前綴表達式又稱爲波蘭式,前綴表達式的運算符位於操作數之前
- 舉例說明:(3+4)*5-6對應的前綴表達式就是 - * + 3 4 5 6
前綴表達式的計算機求值
從右至左掃描表達式,遇到數字時,將數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,用運算符對他們做相應的計算(棧頂元素和次頂元素),並將結果入棧;重複上述過程直達表達式最左端,最後運算的得出的值即爲表達式的結果
思路分析
例如(3+4)*5-6對應的前綴表達式就是 - * + 3 4 5 6,針對前綴表達hi求值步驟如下:
- 從右至左掃描,將6,5,4,3壓入堆棧;
- 遇到+運算符。因此彈出3和4(3爲棧頂元素,4爲次頂元素),計算出3+4的值,得7,再將7入棧;
- 接下來是*運算符,因此彈出7和5,計算7*5=35,將35入棧;
- 最後是-運算符,計算出35-6的值,即29,由此得出最終結果;
中綴表達式
中綴表達式分析與介紹
- 中綴表達式就是最常見的運算表達式如:(3+4)*5-6
- 中綴表達式的求值是我們人最熟悉的,但是對計算機來說卻不好操作(參考:【Java數據結構與算法】棧及經典應用),因此,在計算結果時,往往將中綴表達式轉成其它的表達式來操作(一般轉成後綴表達式)
後綴表達式(逆波蘭表達式)
後綴表達式分析與介紹
- 後綴表達式又稱爲逆波蘭表達式,與前綴表達式相似,只是運算符位於操作數之後
- 舉例說明:(3+4)*5-6對應的後綴表達式就是3 4 + 5 * 6 -
- 再比如:
正常的表達式 | 逆波蘭表達式 |
---|---|
a + b | a b + |
a + (b - c) | a b c - + |
a + (b - c) * d | a b c - d * + |
a + d * (b - c) | a d b c - * + |
a = 1 + 3 | a 1 3 + = |
後綴表達式的計算機求值
從左到右掃描表達式,遇到數字時,將數字壓入堆棧,遇到運算符時,彈出棧頂的兩個數,用運算符對它們做相應的計算(次頂元素和棧頂元素),並將結果入棧;覆上述過程直達表達式最左端,最後運算的得出的值即爲表達式的結果
思路分析逆波蘭計算器
例如(3+4)*5-6對應的前綴表達式就是3 4 + 5 * 6 -,針對後綴表達式求值步驟如下:
- 從左到右掃描,將3和4壓入堆棧;
- 遇到+運算符,因此彈出4和3(4爲棧頂元素,3爲次頂元素),計算出3+4的值,得7,再將7入棧;
- 將5入棧;
- 接下來是運算符,因此彈出5和7,計算出75=35,將35入棧;
- 最後是-運算符,計算出35-6的值,即29,由此得出最終結果;
代碼實現逆波蘭計算器
package DataType;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PolandNotation {
public static void main(String[] args){
//先定義一個逆波蘭表達式
//(3+4)*5-6 =>3 4 + 5 * 6 -
//說明爲了方便,逆波蘭表達式的數字和符號使用空格隔開
String suffixExpression = "3 4 + 5 * 6 -";
//思路
//1.先將"3 4 + 5 * 6 -"放到Array List種
//2.將Array list傳遞給一個方法,遍歷Array list配合棧完成計算
List<String> rpnList = getListString(suffixExpression);
System.out.println("rpnList=" + rpnList);
int res = calculate(rpnList);
System.out.println("計算的結果是=" + res);
}
//將一個逆波蘭表達式,依次將數據和運算符放入Array list中
public static List<String> getListString(String suffixExpression){
//將suffixExpression分割
String[] split = suffixExpression.split(" ");
List<String> list = new ArrayList<String>();
for (String ele : split){
list.add(ele);
}
return list;
}
//完成對逆波蘭表達式的運算
/*
1.從左到右掃描,將3和4壓入堆棧
2.遇到+運算符,因此彈出4和3(4爲棧頂元素,3爲次頂元素),計算出3+4的值,得7,再將7入棧
3.將5入棧
4.接下來是*運算符,因此彈出5和7,計算出7*5=35,將35入棧
5.最後是-運算符,計算出35-6的值,即29,由此得出最終結果
*/
public static int calculate(List<String> ls){
//創建棧,只需要一個棧
Stack<String> stack = new Stack<String>();
//遍歷ls
for (String item : ls){
//這裏使用正則表達式來取出數
if (item.matches("\\d+")){//匹配的是多位數
//入棧
stack.push(item);
}else {
//pop出兩個數,並運算,再入棧
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
int res = 0;
if (item.equals("+")){
res = num1 + num2;
}else if (item.equals("-")){
res = num1 - num2;
}else if (item.equals("*")){
res = num1 * num2;
}else if (item.equals("-")){
res = num1 / num2;
}else {
throw new RuntimeException("運算符有誤~~");
}
//把res入棧
stack.push(res + "");
}
}
//最後留在stack中的結果就是答案
return Integer.parseInt(stack.pop());
}
}
這就是後綴表達式的運算過程,但如果我們只知道中綴表達式不想進行人工轉化應該怎麼計算呢?
中綴表達式轉換爲後綴表達式
可以看到,後綴表達式適合計算式進行運算,但是人卻不太容易寫出,尤其是表達式很長的情況,因此在開發中,我們需要將中綴表達式轉化成後綴表達式。
思路分析
中綴表達式轉後綴表達式的思路步驟分析:
- 初始化兩個棧:運算符棧s1和儲存中間結果的棧s2;
- 從左至右掃描中綴表達式;
- 遇到操作數時,將其壓入s2;
- 遇到運算符時,比較其與s1棧頂運算符的優先級:
- 如果s1爲空,或棧頂運算符爲左括號"(",則直接將此壓入運算符入棧;
- 否則,若優先級比棧頂運算符高,也將運算符壓入s1;
- 否則,將s1棧頂的運算符彈出並壓入到s2中,再次轉到(4-1)與s1中新的棧頂運算符相比較。
- 遇到括號時:
- 如果是左括號,則直接壓入s1;
- 如果是右括號,則依次彈出s1棧頂的運算符,並壓入s2,直到遇到左括號爲止,此時將這一對括號丟棄;
- 重複步驟2-5,直到表達式的最右邊
- 將s1中剩餘的運算符依次彈出並且壓入s2
- 依次彈出s2中的元素並且輸出,結果的逆序即爲中綴表達式對應的後綴表達式。
舉例:中綴表達式1+((2+3)*4)-5 =>後綴表達式
掃描到的元素 | s2(棧頂->棧底) | s1(棧底->棧頂) | 說明 |
---|---|---|---|
1 | 1 | 空 | 數字,直接入棧 |
+ | 1 | + | s1爲空,運算符直接入棧 |
( | 1 | + ( | 左括號,直接入棧 |
( | 1 | + ( ( | 同上 |
2 | 1 2 | + ( ( | 數字 |
+ | 1 2 | + ( ( + | s1棧頂爲左括號。運算符直接入棧 |
3 | 1 2 3 | + ( ( + | 數字 |
) | 1 2 3 + | + ( | 右括號,彈出運算符直至遇到左括號 |
* | 1 2 3 + | + ( * | s1棧頂爲左括號。運算符直接入棧 |
4 | 1 2 3 + 4 | + ( * | 數字 |
) | 1 2 3 + 4 * | + | 右括號,彈出運算符直至遇到左括號 |
- | 1 2 3 + 4 * + | - | -與+優先級相同,因此彈出+,再壓入- |
5 | 1 2 3 + 4 * + 5 | - | 數字 |
到達最右端 | 1 2 3 + 4 * + 5 - | 空 | s1中剩餘的運算符 |
代碼實現
package DataType;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class PostfixExpression
{
public static void main(String[] args) {
//完成將一箇中綴表達式轉成後綴表達式
//1.因爲直接對字符串進行操作不方便,因此先將中綴表達式字符串轉成中綴表達式對應的list
//2.將得到的中綴表達式對應的list轉成後綴表達式對應的list
//即Array list [1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] =>Array list後綴表達式
String expression = "1+((2+3)*4)-5";
List<String> infixExpressionList = toInfixExpressionList(expression);
System.out.println("中綴表達式對應的list=" + infixExpressionList);
List<String> suffixExpreesionList = parseSuffixExpreesionList(infixExpressionList);
System.out.println("後綴表達式對應的list=" + suffixExpreesionList);
}
//即Array list [1, +, (, (, 2, +, 3, ), *, 4, ), -, 5] =>Array list後綴表達式
//方法:將得到的中綴表達式對應的list轉成後綴表達式對應的list
public static List<String> parseSuffixExpreesionList(List<String> ls){
//定義兩個棧
Stack<String> s1 = new Stack<String>();//符號棧
//因爲s2這個棧,在整個過程中,沒有pop操作,而且還得逆序輸出,很麻煩,索引就不用棧了,直接使用list
List<String> s2 = new ArrayList<String>();//存儲中間結果的lists2
//遍歷ls
for (String item : ls){
//如果是一個數就加入到s2
if (item.matches("\\d+")){
s2.add(item);
}else if (item.equals("(")){
s1.push(item);
}else if(item.equals(")")){
//如果是右括號,則依次彈出s1棧頂的運算符,並壓入s2,直到遇到左括號爲止,此時將這一對括號丟棄
while (!s1.peek().equals("(")){
s2.add((s1.pop()));
}
s1.pop();//!!!!!!!!將小括號刪除
}else {
//當item的優先級小於等於s1棧頂運算符的優先級,將s1棧頂的運算符彈出並加入到s2中,再次轉到(4-1)與s1中新的棧頂運算符相比較。
//問題:缺少比較優先級高低的方法
while (s1.size()!=0 && Operation.getValue(s1.peek()) >= Operation.getValue(item)){
s2.add(s1.pop());
}
//還需要將item壓入棧
s1.push(item);
}
}
//將s1中剩餘的運算符依次彈出並且壓入s2
while (s1.size() != 0){
s2.add(s1.pop());
}
return s2;//因爲存放的是list,所以正常輸出就行了
}
//方法:因爲直接對字符串進行操作不方便,因此先將中綴表達式字符串轉成中綴表達式對應的list
public static List<String> toInfixExpressionList(String s){
//定義一個list存放中綴表達式對應的內容
List<String> ls = new ArrayList<String>();
int i = 0;//這是個指針,用於遍歷中綴表達式字符串
String str;//對多位數的拼接
char c;//每遍歷到一個字符,就放入到c
do {
//如果c是一個非數字,需要加入到ls中
if ((c=s.charAt(i)) < 48 || (c=s.charAt(i)) >57){
ls.add("" + c);
i ++;//i需要後移
}else {//如果是一個數,需要考慮多位數的問題
str = "";//先將str 置成""
while (i < s.length() && (c=s.charAt(i)) >= 48 && (c=s.charAt(i)) <= 57){
str += c;//拼接
i ++;
}
ls.add(str);
}
}while (i < s.length());
return ls;//返回
}
}
//編寫一個類Operation,可以返回一個運算符對應的優先級
class Operation{
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 1;
private static int DIV = 1;
//寫一個方法返回對應的優先級
public static int getValue(String operation){
int result = 0;
switch (operation){
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.println("不存在該運算符~");
break;
}
return result;
}
}
畢竟編程我是初學者,難免有理解錯誤的地方,希望大家看完之後,發現錯誤可以評論出來,謝謝大家