逆波蘭表達式實現簡單計算器功能

問題概述

我們完成一個逆波蘭計算器,要求完成如下任務

  1. 輸入一箇中綴表達式,轉成後綴表達式(逆波蘭表達式),使用stack計算結果
  2. 要求支持小括號,和多位整數,我們暫時不考慮小數問題

逆波蘭表達式書寫

逆波蘭表達式(Reverse Polish notation, RPN,或逆波蘭記法),也叫後綴表達式,運算符寫在數值的後面

爲什麼要使用逆波蘭表達式呢

實現逆波蘭表達式其實並不是很難,但是爲什麼要將看似簡單的中綴表達式轉成後綴表達式呢?
其實中綴表達式只是對人類而言是簡單的,然而對於計算機來說,中綴表達式是一個非常複雜的結構。而後綴表達式對於計算機是一個很容易理解的結構。因爲計算即執行的是一個棧結構,執行先進後出的順序

轉後綴表達式步驟
  1. 先準備兩個棧S1,S2,分別用於存儲符號和存儲數值,從中綴表達式的最左邊開始取值。
  2. 當取出的是一個操作數,則直接送入數值棧S2。
  3. 當取出是一個操作符時,如果棧頂的爲空則直接送入操作符棧S1,如果當前操作符級別(不包括括號運算符)大於棧頂操作符時,也直接壓入符號棧S1;當前操作符小於棧頂操作符時,彈出符號棧中的符號送入數值棧S2直至當前操作符級別大於棧頂操作符,將當前操作符壓入操作符棧,當棧頂爲(括號時當前操作符直接壓入符號棧S1中。
  4. 當取出操作符爲(括號時,直接壓入操作符棧中
  5. 當取出操作符爲)括號時,彈出操作符直至棧頂符號爲(,最後將(也彈出。
  6. 重複之前的動作,直至遍歷完算式
  7. 最後逆序打印我們的數值棧S2即可得到我們的逆波蘭表達式。
舉個例子

將1 + ((2 + 3)* 4)- 5 轉成後綴表達式爲1 2 3 + 4 * 5 - +

取出的操作數 S1棧(棧底 --> 棧頂) S2棧(棧底 --> 棧頂) 步驟說明
1 1 將數值1壓入數值棧中
+ + 1 符號棧中爲空,直接壓入符號棧
+ ( 1 操作符爲(直接壓入符號棧中
+ ( ( 1 操作符爲( 直接壓入符號棧中
2 + ( ( 1 2 取出操作數爲數值直接壓入數值棧中
+ + ( ( + 1 2 符號棧的棧頂爲(括號直接壓入符號棧中
3 + ( ( + 1 2 3 取出操作數爲數值直接壓入數值棧中
+ ( 1 2 3 + 取出操作符爲)彈出符號棧中的符號直到彈出最近的(,並將彈出的符號壓入數值棧中
* + ( * 1 2 3 + 取出操作符爲*並且當前操作符級別大於棧頂操作符級別,直接壓入符號棧
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

使用逆波蘭表達式計算結果

  1. 創建一個棧S1用於存儲數值,從左到右遍歷逆波蘭表達式(後綴表達式)
  2. 取出數爲數值壓入棧中
  3. 取出數爲符號時,彈出棧中兩個數值進行計算,並將計算結果壓入數值棧中。
  4. 重複2,3部直至到後綴表達式底部
取出的數值 棧(棧底 – > 棧頂) 步驟說明
1 1 操作數爲數值壓入棧中
2 1 2 操作數爲數值壓入棧中
3 1 2 3 操作數爲數值壓入棧中
+ 1 5 操作數爲符號,彈出兩個數值進行運算,將運算結果壓入棧中
4 1 5 4 操作數爲數值壓入棧中
* 1 20 操作數爲符號,彈出兩個數值進行運算,將運算結果壓入棧中
5 1 20 5 操作數爲數值壓入棧中
- 1 15 操作數爲符號,彈出兩個數值進行運算,將運算結果壓入棧中
+ 16 操作數爲符號,彈出兩個數值進行運算,將運算結果壓入棧中

代碼實現簡單計算器功能

import java.util.List;
import java.util.ArrayList;

public class Calculator {
	public static void main(String[] args) {
		String express = "1+((2+3)*4)-5":
		List<String> suffixExpression = SuffixExpression.toSuffixExpression(list);
        int calculate = SuffixExpression.calculate(suffixExpression);
        System.out.println(calculate);
	}
	
	/**
	* 將一個算式解析成一個個元素
	* @param 需要截取的字符串
	*/
	public static List<String> strToList(String str) {
		if (str == null || str.length() <= 0) {
			throw new IllegalArgumentException();
		}
		List<String> list = new ArrayList<String>();
		for (int i = 0;i < str.length();i++) {
			if (str.charAt() < 48 || str.charAt(i) > 57) {
				list.add("" + str.charAt(i));
			} else {
				String sub = "":
				while (i < str.length() && str.charAt(i) >= 48 && str.charAt(i) <= 57) {
					sub += str.charAt(i);
					list.add(sub);
					i++;
				}
			}
		}
		return list;
	}
	
	/**
	* 將中綴表達式轉逆波蘭表達式(後綴表達式)
	* @param 需要轉的中綴表達式
	*/
	public static List<String> toSuffixExpression(List<String> list) {
		if (list == null || list.isEmpty()) {
			throw new IllegalArgumentException();
		}
		// 用於存放操作符的
		Stack<String> stack = new Stack<String>();
		
		// 用於存放數值,由於最後我們是需要逆序輸出數值棧,所以這裏直接用集合替代棧,可以免去逆序打印
		List<String> suffix = new ArrayList<String>();
		for (String str:list) {
			if (str.matches("\\d")) {
				suffix.add(str);
			} else if (str.equals("(")) {
				stack.push(str);
			} else if (str.equals(")")) {
				while (!stack.peek().equals("(")) {
					suffix.add(stack.pop());
				}
				stack.pop();
			} else {
				while (stack.size() > 0 && getValue(str) < getValue(stack.peek())) {
					suffix.add(stack.pop());
				}
				stack.push(str);
			}
		}
		while (stack.size() > 0){
            suffix.add(stack.pop());
        }
	}
	
	/**
	* 根據逆波蘭表達式計算結果
	* @param 需要計算的逆波蘭表達式
	*/
	public static int calculator(List<String> list) {
		if (list == null || list.isEmpty()) {
			throw new IllegalArgumentException();
		}
		Stack<String> stack = new Stack<String>();
		for (String str:list) {
			if (str.matches("\\d")) {
				stack.push(str);
			} else {
				int num1 = Integer.parseInt(stack.pop());
				int num2 = Integer.parseInt(stack.pop());
				if (str.equals("+")){
                    stack.push(String.valueOf(num1+num2));
                }else if (str.equals("-")){
                    stack.push(String.valueOf(num2-num1));
                }else if (str.equals("*")){
                    stack.push(String.valueOf(num1*num2));
                }else if (str.equals("/")){
                    stack.push(String.valueOf(num2/num1));
                }
			}
		}
		return Integer.parseInt(stack.pop());
	}
	
	/**
	* 計算輸入字符的優先級
	* @param 需要判斷的字符
	*/
	public static int getValue(String str){
        int result;
        switch (str){
            case "+":
                result =  1;
                break;
            case "-":
                result =  1;
                break;
            case "*":
                result =  2;
                break;
            case "/":
                result =  2;
                break;
            default:
               result = 0;
               break;
        }
        return result;
    }
}

代碼寫完,小弟只是能力有限,難免有漏洞,歡迎各路大神指點評論!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章