解析字符串形式的表达式,求解四则运算
要求:
所有数字都是1-9的个位数
运算符包括+、-、*、/、(、和)
运算中间结果和最终结果为double类型
解题思路:
1、常规解法(利用栈):中缀转后缀,再求解;
2、分治法(利用正则匹配):
1)只有加减运算时,从左往右算即可
2)只有乘除运算时,从左往右算即可
3)既有乘除也有加减时,按照“+”和“-”拆分,把乘除运算表达式看做一个整体(当做一个操作数,通过情形 2)算出)
4)有括号时,先求解括号里面的表达式(按照情形 3)求解);
5)有括号嵌套时,从里向外循环求解(每一层都是情形 4));
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Arithmetic {
/**
* 分治法
**/
private final String plusMinusRegex = "[\\+\\-]";
// 9-(1+2*3)*4-(4+1)
public double calcute(final String expression) throws Exception {
StringBuilder expressionBuilder = new StringBuilder(expression);
Pattern pattern = Pattern.compile("\\([\\d\\+\\-\\*/\\.]+\\)");
Matcher matcher = pattern.matcher(expression);
while(matcher.find()) {
int start = matcher.start();
int end = matcher.end();
double r = calWithoutBracket(expressionBuilder.toString().substring(start+1,end-1));
expressionBuilder.replace(start, end, String.valueOf(r));
matcher = pattern.matcher(expressionBuilder.toString());
}
return calWithoutBracket(expressionBuilder.toString());
}
// 4+2*3-5*2
public double calWithoutBracket(final String expression) throws Exception {
double result = 0;
String[] numberStrings = expression.split(plusMinusRegex);
if(numberStrings[0].contains("*") || numberStrings[0].contains("/")) {
result = multiplyAndDivide(numberStrings[0]);
} else {
result = Double.valueOf(numberStrings[0]);
}
int index = 1;
Pattern pattern = Pattern.compile(plusMinusRegex);
Matcher matcher = pattern.matcher(expression);
while(matcher.find()) {
String next = numberStrings[index];
double nextNumber = (next.contains("*") || next.contains("/")) ? multiplyAndDivide(next) : Double.valueOf(next);
String op = matcher.group();
if(op.equals("+")) {
result += nextNumber;
} else if(op.equals("-")) {
result -= nextNumber;
}
index++;
}
return result;
}
// 3.5/2.3*4.1
public double multiplyAndDivide(String expression) {
double result = 1;
String pString = "[\\*\\/]";
String[] numberStrings = expression.split(pString);
if (numberStrings.length > 0) {
result = Double.valueOf(numberStrings[0]);
}
int index = 1;
Pattern pattern = Pattern.compile(pString);
Matcher matcher = pattern.matcher(expression);
while (matcher.find()) {
String op = matcher.group();
if (op.equals("*")) {
result *= Double.valueOf(numberStrings[index]);
} else if (op.equals("/")) {
result /= Double.valueOf(numberStrings[index]);
}
index++;
}
return result;
}
// 1.5-2.8+3
public double plusAndMinus(String expression) {
double result = 0;
String pString = "[\\+\\-]";
String[] numberStrings = expression.split(pString);
if (numberStrings.length > 0) {
result = Double.valueOf(numberStrings[0]);
}
int index = 1;
Pattern pattern = Pattern.compile(pString);
Matcher matcher = pattern.matcher(expression);
while (matcher.find()) {
String op = matcher.group();
if (op.equals("+")) {
result += Double.valueOf(numberStrings[index]);
} else if (op.equals("-")) {
result -= Double.valueOf(numberStrings[index]);
}
index++;
}
return result;
}
public boolean isNumber(char ch) {
return '0' <= ch && '9' >= ch;
}
public int charNumToInt(char num) {
if (num < '0' || num > '9')
throw new IllegalArgumentException(
"argument is not a char of decimal number");
return num - '0';
}
/**
* 中缀转后缀法
**/
public double calcute2(final String expression) {
final String suffixExpression = getSuffixExpression(expression);
Stack<String> stack = new Stack<String>();
for(int i=0; i<suffixExpression.length(); i++) {
char ch = suffixExpression.charAt(i);
if(isNumber(ch)) {
stack.push(String.valueOf(ch));
} else {
double rightOperand = Double.valueOf(stack.pop());
double leftOperand = Double.valueOf(stack.pop());
if(ch == '+') {
stack.push(String.valueOf(leftOperand + rightOperand));
} else if(ch == '-') {
stack.push(String.valueOf(leftOperand - rightOperand));
} else if(ch == '*') {
stack.push(String.valueOf(leftOperand * rightOperand));
} else if(ch == '/') {
stack.push(String.valueOf(leftOperand / rightOperand));
}
}
}
return Double.valueOf(stack.pop());
}
public String getSuffixExpression(final String expression) {
StringBuilder suffixExpression = new StringBuilder();
Stack<Character> operatorStack = new Stack<Character>();
for(int i = 0;i < expression.length(); i++) {
char ch = expression.charAt(i);
if(isNumber(ch)) {
suffixExpression.append(ch);
} else if(ch == '(') {
operatorStack.push(ch);
} else if(ch == ')') {
char topOp;
while((topOp = operatorStack.pop()) != '(') {
suffixExpression.append(topOp);
}
} else {
if(!operatorStack.empty()) {
char topOp = operatorStack.peek();
while(getPriority(topOp) >= getPriority(ch)) {
suffixExpression.append(operatorStack.pop());
if(operatorStack.empty()) {
break;
}
topOp = operatorStack.peek();
}
}
operatorStack.push(ch);
}
}
while(!operatorStack.empty()) {
suffixExpression.append(operatorStack.pop());
}
return suffixExpression.toString();
}
public int getPriority(char operator) {
switch (operator) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
default:
return -1;
}
}
}
测试代码:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
public class ArithmeticTest {
private Arithmetic arithmetic;
@Before
public void setUp() throws Exception {
arithmetic = new Arithmetic();
}
@Test
public void testIsNumber() throws Exception {
assertTrue(arithmetic.isNumber('0'));
assertTrue(arithmetic.isNumber('9'));
assertTrue(arithmetic.isNumber('2'));
assertFalse(arithmetic.isNumber('+'));
}
@Test
public void testCharNumToInt() throws IllegalArgumentException{
assertTrue(arithmetic.charNumToInt('2') == 2);
try {
arithmetic.charNumToInt('+');
assertTrue(false);
} catch (IllegalArgumentException e) {
assertTrue(true);
}
}
@Test
public void testPlusAndMinus() throws Exception {
assertTrue(arithmetic.plusAndMinus("8.5-3.2+2") == 7.3);
}
@Test
public void testMultiplyAndDivide() throws Exception {
assertTrue(arithmetic.multiplyAndDivide("4.0/2*4.1") == 8.2);
}
@Test
public void testCalWithoutBracket() throws Exception {
assertTrue(arithmetic.calWithoutBracket("4") == 4.0);
}
@Test
public void testCalcute() throws Exception {
assertTrue(arithmetic.calcute("(((5+2)*2)-(5+3))/2") == 3.0);
}
@Test
public void testGetSuffixExpression() throws Exception {
assertEquals(arithmetic.getSuffixExpression("9+7*4-(4+1)"),"974*+41+-");
}
@Test
public void testCalcute2() throws Exception {
assertTrue(arithmetic.calcute2("(2*(4+1))") == 10.0);
}
}