牛客HJ54 表达式求值 js(参考C++代码栈&队列)

221 阅读4分钟
题目描述:

给定一个字符串描述的算术表达式,计算出结果值。
输入字符串长度不超过 100 ,合法的字符包括 ”+, -, *, /, (, )” , ”0-9” 。
数据范围:运算过程中和最终结果均满足 ∣𝑣𝑎𝑙∣≤2^31−1,即只进行整型运算,确保输入的表达式合法

在查看其他人以栈为参考答案的过程中,发现用栈的形式后进先出的方式进行计算会有问题。
比如2/32,按照数字栈和运算符栈的调用,会先计算32,再计算2/6;
由比如1-0-6,会先计算0-6=-6,1-(-6)=7,显然是有问题的。
但是有一句话说的很对,从左往右计算、优先乘除、然后加减。实际机考使用eval()!
此参考答案参考牛客中使用堆栈写法的大佬,用js仿照其思路进行的仿写(其答案截图如下,具体思路可以去他的答案里面看)。

image.png
写的如果有问题,请各位大佬指导。

主逻辑
      //测试案例
      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);
      }