四则运算的java实现

很简单的一个四则运算,比如 4+3.9*(7-2*3.6),你很快就可以给出答案,但是对于计算机来说,他是怎么运算的,你可能会说,先算括号里的,再算乘除法,最后算加减法。不错,你确实挺喜欢这种算法,但是计算机不喜欢,因为按照你的算法,计算机需要反复的遍历和计算后才能给出结果,效率十分低下。

那计算机是如何实现四则运算的呢?

波兰的一位叫做卢卡西维奇的逻辑学家发明了一种表示表达式的方法,称为逆波兰式,也叫后缀表达式。平时我们所看到的四则运算,都是中缀表达式,因为运算符总是处在数字的中间。计算机首先将我们常见的中缀表达式转换为后缀表达式,然后再进行运算。转换的过程如下:

1. 首先初始化一个栈结构;

2. 然后从左到右遍历中缀表达式,遇到数字,追加到后缀表达式中;如果遇到左括号,则将其入栈;如果遇到右括号,则执行出栈操作,依次将出栈元素追加到后缀表达式,直到出栈元素是左括号为止,左括号不追加到后缀表达式中;如果是加减乘除,就依次弹出所有优先级大于或者等于该运算符的栈顶元素(栈中肯定没有右括号,认为左括号的优先级最低),然后将该运算符入栈;

3. 遍历完中缀表达式之后,清空栈,并依次将出栈元素追加到后缀表达式中。

这样,计算机就把中缀表达式转换成了后缀表达式。然后就开始计算后缀表达式的值,过程如下:

1. 同样初始化一个栈结构,或者直接使用刚才的栈;

2. 遍历后缀表达式,遇到数字就入栈;遇到运算符则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,运算后的结果再入栈,直到后缀表达式遍历完毕;

3. 遍历完后缀表达式之后,栈中必然只有一个元素,就是我们要的结果了。

下面是用java代码实现四则运算的代码,大家有兴趣可以试一下。

package com.tansun;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class CalculateTest {

	public static void main(String[] args) {
		String str = "0.34 +3.9*(7 - 2*  3.6) /0.66+8-(9.3+8/3)";
		BigDecimal result = cal(str);
		System.out.println(result);
	}

	public static BigDecimal cal(String str) {
		// 对表达式进行预处理,并简单验证是否是正确的表达式
		// 存放处理后的表达式
		List<String> list = new ArrayList<>();
		char[] arr = str.toCharArray();
		// 存放数字临时变量
		StringBuffer tmpStr = new StringBuffer();
		for (char c : arr) {
			// 如果是数字或小数点,添加到临时变量中
			if (c >= '0' && c <= '9') {
				tmpStr.append(c);
			} else if (c == '.') {
				if (tmpStr.indexOf(".") > 0) {
					throw new RuntimeException("非法字符");
				}
				tmpStr.append(c);
			}
			// 如果是加减乘除或者括号,将数字临时变量和运算符依次放入list中
			else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {
				if (tmpStr.length() > 0) {
					list.add(tmpStr.toString());
					tmpStr.setLength(0);
				}
				list.add(c + "");
			}
			// 如果是空格,跳过
			else if (c == ' ') {
				continue;
			} else {
				throw new RuntimeException("非法字符");
			}
		}
		if (tmpStr.length() > 0) {
			list.add(tmpStr.toString());
		}
		// 初始化后缀表达式
		List<String> strList = new ArrayList<>();
		// 运算过程中,使用了两次栈结构,第一次是将中缀表达式转换为后缀表达式,第二次是计算后缀表达式的值
		Stack<String> stack = new Stack<>();
		// 声明临时变量,存放出栈元素
		String tmp;
		// 1. 将中缀表达式转换为后缀表达式
		for (String s : list) {
			// 如果是左括号直接入栈
			if (s.equals("(")) {
				stack.push(s);
			}
			// 如果是右括号,执行出栈操作,依次添加到后缀表达式中,直到出栈元素为左括号,左括号和右括号都不添加到后缀表达式中
			else if (s.equals(")")) {
				while (!(tmp = stack.pop()).equals("(")) {
					strList.add(tmp);
				}
			}
			// 如果是加减乘除,弹出所有优先级大于或者等于该运算符的栈顶元素(栈中肯定没有右括号,认为左括号的优先级最低),然后将该运算符入栈
			else if (s.equals("*") || s.equals("/")) {
				while (!stack.isEmpty()) {
					// 取出栈顶元素
					tmp = stack.peek();
					if (tmp.equals("*") || tmp.equals("/")) {
						stack.pop();
						strList.add(tmp);
					} else {
						break;
					}
				}
				stack.push(s);
			} else if (s.equals("+") || s.equals("-")) {
				while (!stack.isEmpty()) {
					// 取出栈顶元素
					tmp = stack.peek();
					if (!tmp.equals("(")) {
						stack.pop();
						strList.add(tmp);
					} else {
						break;
					}
				}
				stack.push(s);
			}
			// 如果是数字,直接添加到后缀表达式中
			else {
				strList.add(s);
			}
		}
		// 最后依次出栈,放入后缀表达式中
		while (!stack.isEmpty()) {
			strList.add(stack.pop());
		}
		// 2.计算后缀表达式的值
		Stack<BigDecimal> newStack = new Stack<>();
		for (String s : strList) {
			// 若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,
			// 运算后的结果再进栈,直到后缀表达式遍历完毕
			if (s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {
				BigDecimal b1 = newStack.pop();
				BigDecimal b2 = newStack.pop();
				switch (s) {
				case "+":
					newStack.push(b2.add(b1));
					break;
				case "-":
					newStack.push(b2.subtract(b1));
					break;
				case "*":
					newStack.push(b2.multiply(b1));
					break;
				case "/":
					newStack.push(b2.divide(b1, 9, BigDecimal.ROUND_HALF_UP));
					break;
				}
			}
			// 如果是数字,入栈
			else {
				newStack.push(new BigDecimal(s));
			}
		}
		// 最后,栈中仅有一个元素,就是计算结果
		return newStack.peek();
	}

}

猜你喜欢

转载自blog.csdn.net/jia_costa/article/details/79042679