题目描述
输入4则运行算表达式, 额外再定义了两种运算操作
MIN(a,b) 计算最小值
MAX(a,b) 计算最大值
表达式不包括空格, 可以使用()设置优先级, 数字都是整数, 且输入一定合法, 不需要校验
例如 MIN(1,(2+3)*4)+(1+2)*3 的值为10
思路
js实现
打开 https://js.do/, 将以下代码复杂上去, 就可以直接运行了
// 用于字符串转整数
const CharCode_0 = '0'.charCodeAt(0);
const CharCode_9 = '9'.charCodeAt(0);
// 扩展数组接口
Array.prototype.peek = function () {
return this[this.length-1];
}
const operations = {
'+': function(a, b) {
return a + b;
},
'-': function(a, b) {
return a - b;
},
'*': function(a, b) {
return a * b;
},
'/': function(a, b) {
return Math.floor(a / b);
},
'I': function(a, b) { // MIN
return Math.min(a, b);
},
'A': function(a, b) { // MAX
return Math.max(a, b);
},
}
// 简化字符遍历
class StringBuffer {
constructor(str){
this.str = str;
this.pos = 0;
this.len = str.length;
}
hasNext() {
return this.pos < this.len;
}
peek() {
return this.str.charAt(this.pos);
}
poll() {
return this.str.charAt(this.pos++);
}
skip(n) {
this.pos += n;
}
back(n) {
this.pos -= n;
}
// 读取一个整数, 假设当前字节一定是个数字
readNumber() {
let n = 0;
let c;
while(this.pos < this.len) {
c = this.str.charCodeAt(this.pos);
if (c >= CharCode_0 && c <= CharCode_9) {
n = n * 10 + (c - CharCode_0);
this.pos++;
}else{
break;
}
}
return n;
}
}
// 计算表达式
function doEval(buf) {
// 分别记录操作符和操作数
const operatorStack = [];
const operandStack = [];
// 用于判断左括号之后, 直接出现正负符号
let lastOpIsLeftParetness = false;
loop:
while (buf.hasNext()) {
const ch = buf.poll();
let parentness = false;
switch(ch) {
case 'M': {
// MIN, MAX操作
const op = buf.poll(); // read I or A
buf.skip(2); // skip N/X(
// 第一个参数是遇到逗号结束
const a = doEval(buf);
// 第二个参数是遇到多出来的右括号结束
const b = doEval(buf);
const r = operations[op](a, b);
operandStack.push(r);
}
break;
case '+':
case '-':{
if( operandStack.length === 0 || lastOpIsLeftParetness) {
// +/-表示符号
operandStack.push(0);
}
// 这之前的操作符可能的情况有
// 1. 1个加减,跟随1个乘除, 从后往前消两次
// 2. 1个加减操作, 消掉
// 3. 1个乘除操作, 消掉
// 不可能出现多个连续同级操作
calc(operatorStack, operandStack);
operatorStack.push(ch);
}
break;
case '*':
case '/':{
// 这之前的操作符可能的情况有
// 1. 1个加减,跟随之1个乘除, 只能消掉乘除操作
// 2. 1个加减操作, 不能消
// 3. 1个乘除操作, 消掉
while (operatorStack.length > 0 ) {
const op = operatorStack.peek();
if (op === '*' || op === '/') {
operatorStack.pop();
const b = operandStack.pop();
const a = operandStack.pop();
const r = operations[op](a, b);
operandStack.push(r);
} else {
break;
}
}
operatorStack.push(ch);
}
break;
case '(': {
operatorStack.push(ch);
parentness = true;
}
break;
case ')': {
if(!operatorStack.includes('(')) {
// 遇到右括号, 却没有左括号, 结束
break loop;
}
// 计算圆括号内的表达式
while (operatorStack.length > 0 ) {
const op = operatorStack.pop();
if (op === '(') {
break;
}
const b = operandStack.pop();
const a = operandStack.pop();
const r = operations[op](a, b);
operandStack.push(r);
}
}
break;
case ',': {
// MIN/MAX的第一个参数结束标志
break loop;
}
default: {
// 遇到数字
buf.back(1);
const num = buf.readNumber();
operandStack.push(num);
}
}
lastOpIsLeftParetness = parentness;
}
calc(operatorStack, operandStack);
return operandStack.pop();
}
function calc(operatorStack, operandStack) {
while (operatorStack.length > 0 ) {
const op = operatorStack.peek();
if (op === '(') {
break;
}
operatorStack.pop();
const b = operandStack.pop();
const a = operandStack.pop();
// 注意a, b的先后顺序
const r = operations[op](a, b);
operandStack.push(r);
}
}
function evaluate(express) {
return doEval(new StringBuffer(express));
}
const thiz = this;
function main() {
const expressions = [
['1+2+3+4', 10],
['1*2*3*4', 24],
['1+2+3+4+1*2*3*4', 34],
['1+(2+3+4+1*2*3*4+2+3+4+1)/3*4', 57],
['1+2*3', 7],
['1+(2)*3', 7],
['1+(-2)*3', -5],
['(1+(-2))*3', -3],
['(1+2)*3', 9],
['1+1/2*3', 1],
['1+3/2*3', 4],
['(1+(1+2)*3)/2', 5],
['MAX(1+2*3,(1+2)*3)', 9],
['MAX(MAX(1+2*3,(1+2)*3),1)', 9],
]
const output = [];
for( const expr of expressions ) {
const result = evaluate(expr[0]);
output.push(`${expr[0]} = ${result}, expect: ${expr[1]}, ${result===expr[1]?'OK':'Error'}`);
}
console.log(output.join('\r\n'));
// 浏览器上运行
if(thiz.document) {
thiz.document.write(output.join('
'));
}
}
main();