题目描述:
给定一个字符串描述的算术表达式,计算出结果值。
输入字符串长度不超过 100 ,合法的字符包括 ”+, -, *, /, (, )” , ”0-9” 。
数据范围:运算过程中和最终结果均满足 ∣𝑣𝑎𝑙∣≤2^31−1,即只进行整型运算,确保输入的表达式合法
在查看其他人以栈为参考答案的过程中,发现用栈的形式后进先出的方式进行计算会有问题。
比如2/32,按照数字栈和运算符栈的调用,会先计算32,再计算2/6;
由比如1-0-6,会先计算0-6=-6,1-(-6)=7,显然是有问题的。
但是有一句话说的很对,从左往右计算、优先乘除、然后加减。实际机考使用eval()!
此参考答案参考牛客中使用堆栈写法的大佬,用js仿照其思路进行的仿写(其答案截图如下,具体思路可以去他的答案里面看)。
写的如果有问题,请各位大佬指导。
主逻辑
//测试案例
let line = "2+(4*5+1)*8+7*4+10-9+3+1*9-(6-0*4*4)"; //17
//去掉空格
line = line.replace(/\s+/g, "");
//console.log(line);
let res = 0; //初始化结果变量
let numFlag = false; //判断当前元素是否为数字的bool
//1.切分字符串,切为符号和数字
let arr = line.split(""); //切分字符串
// console.log(arr);
//2.拼数字
let handleArr = joinNumber(arr, numFlag);
console.log(handleArr);
//3.调用函数获得结果
res = calculateWithBracket(handleArr);
console.log(res);
递归逻辑
//开始计算 用数组替代队列的形式
//带括号的计算
function calculateWithBracket(arr) {
let bracketResult = 0;
//把带有括号的表达式分割成数组单独进入函数中
//includes找到指定元素会停止遍历
while (arr.includes("(")) {
let amount = arr.filter((item) => item === "(").length;
if (amount === 1) {
let startIndex = arr.indexOf("(");
let endIndex = arr.lastIndexOf(")");
let subArr = arr.slice(startIndex + 1, endIndex); //[start,end)
bracketResult = calculateWithBracket(subArr);
arr.splice(startIndex, endIndex - startIndex + 1, bracketResult); //替换掉括号表达式
} else {
//当(>1时,需要一点一点找 (())/()+()
let startIndex = arr.indexOf("(");
let layer = 1; //括号层数
for (let i = startIndex + 1; i < arr.length; i++) {
if (arr[i] === "(") {
layer++;
} else if (arr[i] === ")") {
layer--;
}
if (layer === 0) {
let endIndex = i;
let subArr = arr.slice(startIndex + 1, endIndex); //[start,end)
bracketResult = calculateWithBracket(subArr);
arr.splice(
startIndex,
endIndex - startIndex + 1,
bracketResult
); //替换掉括号表达式
break;
}
}
}
}
//跳出循环时,一定没有括号
return calculateWithoutBracket(arr);
}
//不带括号的计算--基础计算用于递归
function calculateWithoutBracket(arr) {
let numArr = []; //数字数组
let opArr = []; //运算符数组
//判断是否内嵌数组
let includeArr = false;
//判断数字前是否有负号
let negative = false;
let j = 0; //用于判断运算符是否相邻增加
for (let i = 0; i < arr.length; i++) {
//如果是数字,压入数字数组
if (!isOperator(arr[i])) {
//待补充0的情况
if (!includeArr) {
numArr.push(Number(arr[i]));
} else {
//如果内嵌数组还存在,则把数字压入内嵌数组中
numArr[numArr.length - 1].push(Number(arr[i]));
}
} else {
//如果是运算符 +-*/
if (arr[i] === "-" || arr[i] === "+") {
//意味着*/的运算==内嵌数组结束
if (includeArr) {
//按照从左到右的计算顺序计算结果
let result = doubleNumCalculateArr(numArr.pop(), opArr.pop());
numArr.push(result);
includeArr = false;
}
//如果负号前面是一个运算符,则将负号视为负数
if (isOperator(arr[i - 1]) && arr[i] === "-") {
negative = true;
j = i; //获取当前增加的运算符的索引
}
opArr.push(arr[i]);
} else if (arr[i] === "*" || arr[i] === "/") {
//额外增加一个数组
if (!includeArr) {
let lastNum = numArr.pop();
numArr.push([lastNum]);
opArr.push([arr[i]]);
includeArr = true;
} else {
opArr[opArr.length - 1].push(arr[i]);
}
}
//判断第一个是不是负号
if (i === 0 && opArr[0] === "-") {
negative = true;
}
}
//把类似-1开头的表达式修正,因为先计算了括号内的所以-()这种类型已经解决掉
if (negative && i === 1) {
numArr[0] = -numArr[0];
negative = false;
opArr.shift();
}
//如果后续的负数不是用括号包裹的 3*-4 这种情况
if (j === i - 1 && negative) {
let lastIndex = numArr.length - 1;
numArr[lastIndex] = -numArr[lastIndex];
negative = false;
opArr.pop(); //删除减号
j = 0; //重置索引
}
}
if (includeArr) {
let result = doubleNumCalculateArr(numArr.pop(), opArr.pop());
numArr.push(result);
includeArr = false;
}
while (opArr.length != 0) {
doubleNumCalculate(numArr, opArr);
}
return numArr.pop(); //返回最后一个元素
}
工具函数
//1.把拆分成散的数字拼成对应整数
function joinNumber(arr, numFlag) {
let handleArr = [];
let j = 0; //handleArr索引
for (let i = 0; i < arr.length; i++) {
if (isOperator(arr[i])) {
if (numFlag) {
j++;
}
numFlag = false;
handleArr.push(arr[i]); //如果是符号直接加
j++;
} else {
if (!numFlag) {
numFlag = true;
handleArr.push(arr[i]); //加入第一位数
} else {
handleArr[j] += arr[i];
}
}
}
arr = null; //释放数组
return handleArr;
}
//2.判断是否是一个运算符
function isOperator(n) {
if (
n === "+" ||
n === "-" ||
n === "*" ||
n === "/" ||
n === "(" ||
n === ")"
) {
return true;
} else {
return false;
}
}
//3.计算两个数组中的数的运算结果
function doubleNumCalculateArr(numArr, opArr) {
while (opArr.length != 0) {
//从左往右计算
doubleNumCalculate(numArr, opArr);
}
return numArr.pop(); //返回第一个元素同时将数组清空
}
//4.计算两个数的运算结果
function doubleNumCalculate(numArr, opArr) {
let a = numArr.shift();
let b = numArr.shift();
let operator = opArr.shift();
let temp = 0;
switch (operator) {
case "+":
temp = a + b;
break;
case "-":
temp = a - b;
break;
case "*":
temp = a * b;
break;
case "/":
temp = a / b;
break;
}
numArr.unshift(temp);
}