题目说明
在各个数字之间插入四则运算的运算符组成算式,
然后计算算式的结果(某些数位之间可以没有运算符,但整个表达式中最少要插入 1 个运算符)。
例如:
1234 -> 1+2×3-4 = 3
9876 -> 9×87+6 = 789
假设这里的条件是,组合算式的计算结果为“将原数字各个数位上的数逆序排列得到的数”,
并且算式的运算按照四则运算的顺序进行(先乘除,后加减)。
那么位于 100~999,符合条件的有以下几种情况。
351 -> 3×51 = 153
621 -> 6×21 = 126
886 -> 8×86 = 688
求:位于 1000~9999,满足上述条件的数。
思路
1.罗列出1000~9999之间的每个数字
2.拆分数字,与运算符组合,拼接成表达式
3.计算表达式的值(①中缀表达式=>后缀表达式(逆波兰表示法) ②计算后缀表达式的值)
4.与第一步罗列出的数字的逆序数字进行比较,如果相等,则输出结果
*5.因为4位数拆成多个数相-,/得到的结果都不足4位;相+若要得到4位数(xxx+x=xxxx),则千位和百位必然是9,又因所求结果为逆序后的数字,所以个位和十位也是9,即999+9=1008,并不符合要求,所以有效的运算符只有乘
代码
public static void main(String[] args) {
String[] op = {"+", "-", "*", "/", ""}; // 作为数字之间的分隔符(可根据思路第5步优化为{"*",""})
// 变量n依次表示指定范围内的每个数字
for (int n = 1000; n < 10000; n++) { // 四位数中间有3个位置可以插入运算符或者""(不插入)
String num = "" + n; // 将数字转为字符串形式,以便通过正则表达式拆分
if (num.matches("\\d*0+$")) continue; // 以0结尾的四位数,反转后不足4位数,直接跳过,从而减少循环次数。
Double expected = Double.parseDouble(new StringBuilder(num).reverse().toString()); // 反转后的数字:期望值
// 罗列出3个位置上的运算符(或"")
for (int i = 0; i < op.length; i++) {
for (int j = 0; j < op.length; j++) {
for (int k = 0; k < op.length; k++) {
// 拼接出算数表达式
String exp = num.charAt(0) + op[i] + num.charAt(1) + op[j] + num.charAt(2) + op[k] + num.charAt(3);
Double res = getResult(exp); // 计算表达式的值,自定义的方法getResult(xx)
// 与期望值比较,相同则输出结果
if (Double.compare(expected, res) == 0) { // java.lang.NullPointerException
System.out.println(num + " -> " + res); // 最终结果:5931 -> 1395.0
}
}
}
}
}
}
// 按照四则运算优先级,计算表达式的值,并返回
private static Double getResult(String exp) {
/* 1.中缀表达式 --> 后缀表达式
1) 通过正则表达式切分算数表达式
2) 从左往右遍历中缀表达式的每个元素:数字和运算符
3) 若是数字,直接存入List
4) 若是符号,则①判断优先级②出栈③入栈:
i. 判断当前符号与栈顶符号(最近一次存入的符号)的优先级
ii. 如果该符号是右括号,或者优先级低于栈顶符号,则栈顶元素依次出栈并存入StringBuilder
iii. 然后将当前符号入栈
5) 遍历结束后,出栈所有运算符
*/
Stack<Double> nums = new Stack<>(); // 存放数字
Stack<String> op = new Stack<>(); // 存放符号
List<String> list = new LinkedList<String>(); // 保存数字和+-*/符号
// 拆分出数字和操作符
// \d+(\.\d+)? 表示数字部分,小括号里的是小数部分,?表示0~1次
// (?<!\d)-? 表示数字部分前面的负号;?表示负号可以没有,或者有一个;(?<!\d)表示不能以数字开头,即这个负号的前面不能是数字(否则-就是减号,而不是负号了)
Pattern p = Pattern.compile("(?<!\\d)-?\\d+(\\.\\d+)?|[+\\-*/()]"); // Java中的正则表达式用\\表示转义字符
Matcher m = p.matcher(exp);
while (m.find()) {
String s = m.group(); // 依次找出每个元素(数字or字符)
// 操作符
if (s.matches("[+\\-*/()]")) {
switch (s) {
case "(": // "(" 入栈
op.push("(");
break;
case ")": // ")" 出栈,直到"("
String top = null; // 栈顶的运算符
while (!(top = op.pop()).equals("(")) {
list.add(top);
}
break;
default: // 四则运算符,比较优先级,出栈比当前符号优先级高的符号,然后入栈当前符号
while (!op.empty() && opPriority(s) <= opPriority(op.peek())) { // 自定义的方法opPriority(xx)
list.add(op.pop()); // 出栈
}
op.push(s); // 入栈当前运算符
break;
}
} else { // 数字
list.add(s);
}
}
// 出栈所有运算符
while (!op.isEmpty()) {
list.add(op.pop());
}
//------- 至此,后缀表达式的每个部分均已存入list ------
// 2.计算后缀表达式的值
Double res = NaN; // not a number; 需要导包import static java.lang.Double.NaN;
for (String e : list) { // 依次取出list中的每个操作数和运算符
if (e.matches("[+\\-*/()]")) { // +-*/运算符,取出nums栈顶的两个数字进行运算,然后将结果存入nums
double next = nums.pop();
double pre = nums.pop();
res = calc(pre, next, e); // 自定义的方法calc(xx)
nums.push(res);
} else { // 数字,直接存入nums
nums.push(Double.parseDouble(e));
}
}
return res;
}
// 四则运算
private static double calc(double pre, double next, String op) {
switch (op) {
case "+":
return pre + next;
case "-":
return pre - next;
case "*":
return pre * next;
case "/":
return pre / next; // Java中两个double相除,0为除数不会报错
default:
break;
}
throw new IllegalArgumentException("不支持的操作符!");
}
// 计算运算符的优先级。此处指定:数字越大,优先级越高
private static int opPriority(String op) {
if (op == null) return 0;
switch (op) {
case "(":
return 1;
case "+":
case "-":
return 2;
case "*":
case "/":
return 3;
default:
throw new IllegalArgumentException("不支持的操作符!");
}
}
结果
5931
学习网址
【Java实现四则运算表达式求值(逆波兰法)】https://blog.csdn.net/qq_22795957/article/details/105872740