java实现计算复杂数学表达式

用过python或者PHP的同学应该知道,它们都有个eval函数,这个函数真的是好用啊,比如我算数的时候就喜欢用python的控制台来当做计算器。比如:

Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> print('公积金每年存多少?',3500*0.12*2*12)
公积金每年存多少? 10080.0
>>> print('余额宝年利率0.27的话,一万每天利息多少?',1*270/365)
余额宝年利率0.27的话,一万每天利息多少? 0.7397260273972602
>>> 400+10/(120-20)+400
800.1
>>>

但java貌似没有。

以下是我自己的实现。

思路:

思路就是分两步来进行

  1. 翻译输入的数学表达式,也就是中缀表达式转后缀表达式。例如 a+b*(c-d) 转为后缀表达式就是 abcd-*+
  2. 对后缀表达式计算结果。这里用到了栈存储计算结果,每次都是对两个数计算,例如 abcd-*+ ,计算方法是先从头遍历,数字直接入栈,当遇到计算符,则从栈顶取出来两个数计算然后再把结果压栈,最终全部计算完之后栈里面只剩下一个元素就是结果。

1.中缀表达式转后缀表达式

实现转换的基本步骤如下:

  1. 初始化一个运算符栈。
  2. 从算数表达式输入的字符串中依次从左向右每次读取一个字符。
  3. 如果当前字符是操作数,则直接填写到后缀表达式。
  4. 如果当前字符是(左括号,将其压入运算符栈(第一步定义)。
  5. 如果当前字符为运算符,则
    1. 当运算符栈为空,则将其压入运算符栈。
    2. 当此运算符的优先级高于栈顶元素的时候,则将此运算符压入运算符栈;否则,弹出栈顶运算符到后缀表达式,并且将当前运算符压栈。回到步骤2.
  6. 如果当前字符是)右括号,反复将栈顶元素弹出到后缀表达式,直到栈顶元素是左括号(为止,并将左括号从栈中弹出丢弃。
  7. 如果读取还未完成,回到步骤2.
  8. 如果读取完成,则将栈中剩余的运算符依次弹出到后缀表达式。
private static String transfer(String mathStr) {
		// 标记输出结果
		StringBuilder result = new StringBuilder();
		// 1.初始化一个运算符栈。
		Stack<Character> stack = new Stack();
		if (mathStr == null || mathStr.length() == 0) {
			return null;
		}
		System.out.println("--------------");
		System.out.println("中缀表达式:" + mathStr);
		char[] arr = mathStr.toCharArray();
		for (int i = 0; i < arr.length; i++) {
			// 2.从算数表达式输入的字符串中依次从左向右每次读取一个字符。
			char s = arr[i];
			// 3.如果当前字符是操作数,则直接填写到后缀表达式。
			if (Character.isDigit(s)) {
				result.append(s);
			}
			// 4.如果当前字符是(左括号,将其压入运算符栈(第一步定义)。
			else if ('(' == s) {
				stack.push(s);
			}
			// 5.如果当前字符为运算符,则
			else if ('+' == s || '-' == s || '*' == s || '/' == s) {
				if (!stack.isEmpty()) {
					char stackTop = stack.pop();
					// 当此运算符的优先级高于栈顶元素的时候,则将此运算符压入运算符栈
					if (compare(s, stackTop)) {
						stack.push(stackTop);
						stack.push(s);
					}
					// 否则,弹出栈顶运算符到后缀表达式,并且将当前运算符压栈。回到步骤2.
					else {
						result.append(stackTop);
						stack.push(s);
					}
				}
				// 5.1.当运算符栈为空,则将其压入运算符栈。
				else {
					stack.push(s);
				}
			}
			// 6.如果当前字符是)右括号,反复将栈顶元素弹出到后缀表达式,直到栈顶元素是左括号(为止,并将左括号从栈中弹出丢弃。
			else if (s == ')') {
				while (!stack.isEmpty()) {
					char item = stack.pop();
					if (item != '(') {
						result.append(item);
					} else {
						break;
					}
				}
			}
		}
		while (!stack.isEmpty()) {
			result.append(stack.pop());
		}
		System.out.println("后缀表达式:" + result.toString());
		return result.toString();
	}
  //比较优先级
  private static boolean compare(char s, char item) {
  		if (item == '(') {
  			return true;
  		}
  		if (s == '*' || s == '/') {
  			if (item == '+' || item == '-') {
  				return true;
  			}
  		}
  		return false;
  	}

2.计算结果

每次都是对两个数计算,例如 abcd-*+ ,计算方法是先从头遍历,数字直接入栈,当遇到计算符,则从栈顶取出来两个数计算然后再把结果压栈,最终全部计算完之后栈里面只剩下一个元素就是结果。

private static int calculate(String transferToPostfix) {
		Stack<Integer> stack = new Stack();
		char[] c = transferToPostfix.toCharArray();
		int a, b;
		for (int i = 0; i < c.length; i++) {
			switch (c[i]) {
			case '+':
				a = Integer.valueOf(stack.pop().toString());
				b = Integer.valueOf(stack.pop().toString());
				stack.push(b + a);
				break;
			case '-':
				a = Integer.valueOf(stack.pop().toString());
				b = Integer.valueOf(stack.pop().toString());
				stack.push(b - a);
				break;
			case '*':
				a = Integer.valueOf(stack.pop().toString());
				b = Integer.valueOf(stack.pop().toString());
				stack.push(b * a);
				break;
			case '/':
				a = Integer.valueOf(stack.pop().toString());
				b = Integer.valueOf(stack.pop().toString());
				stack.push(b / a);
				break;

			default:
				Character d = c[i];
				stack.push(Integer.valueOf(d.toString()));
				break;
			}
		}
		return stack.pop();
	}

3.测试

public static void main(String[] args) {
  System.out.println("计算结果:" + calculate(transferToPostfix("1+2*(3-4)")));
  System.out.println("计算结果:" + calculate(transferToPostfix("3-2/3")));
  System.out.println("计算结果:" + calculate(transferToPostfix("2*3-9/(4-1)")));
}
--------------
中缀表达式:1+2*(3-4)
后缀表达式:1234-*+
计算结果:-1
--------------
中缀表达式:3-2/3
后缀表达式:323/-
计算结果:3
--------------
中缀表达式:2*3-9/(4-1)
后缀表达式:23*941-/-
计算结果:3

这里我使用了char来存储运算单位,因此只能运算个位数,如果要运算多位数或者是带小数的,把char类型换成自己封装的就可以了,思路都是一样的。

  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

痕迹丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值