逆波兰表达式实现简单计算器功能

问题概述

我们完成一个逆波兰计算器,要求完成如下任务

  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;
    }
}

代码写完,小弟只是能力有限,难免有漏洞,欢迎各路大神指点评论!!!

发布了7 篇原创文章 · 获赞 3 · 访问量 270

猜你喜欢

转载自blog.csdn.net/justLym/article/details/104550194