题解 | #表达式求值#

表达式求值

http://www.nowcoder.com/practice/c215ba61c8b1443b996351df929dc4d4

宫水三叶 大佬的代码思路进行编写的 JS 版。
通过一些函数式编程的思想进行优化,代码格式不大清晰建议 copy 到本地 IDE 看。

/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 * 返回表达式的值
 * @param s string字符串 待计算的表达式
 * @return int整型
 */
function solve (s) {
  // 判断是否为数字
  const isNum = c => +c >= 0 && +c <= 9;
  // 计算的二次封装,迭代计算
  // 如果想终止迭代计算,请返回 false
  const iteratorCalc = (optStack, callback) => {
    // 依据内容进行计算,计算成功返回 true
    const calc = (nums, opts) => {
      // 我们在封装一个函数时,需要确保自己的安全性
      // 因为我们不能确保传入的 nums 或 opts 是合法的
      // 所以下面这两个 if 判断并不是非必要的
      if (nums.length < 2) return false;
      if (!opts.length || opts[opts.length - 1] === "(") return false;
      let num2 = nums.pop();
      let num1 = nums.pop();
      let opt = opts.pop();
      switch (opt) {
        case "+":
          num1 += num2;
          break;
        case "-":
          num1 -= num2;
          break;
        case "*":
          num1 *= num2;
          break;
        case "/":
          num1 = ~~(num1 / num2);
          break;
      }
      nums.push(num1);
      return true;
    };
    // 运算符优先级
    const optPrioMap = {
      "-": 1,
      "+": 1,
      "*": 2,
      "/": 2
    };
    while (optStack.length && callback(calc, optPrioMap));
  };
  // 预处理字符串,替换所有的 ' '
  s = s.replaceAll(' ', '');
  const n = s.length;
  // 这里 numStack 初始化时,添加了一个 0 
  // 用于预防 + 1 或者 - 1 开头的情况, 添加 0 后就变为 0 + 1 或 0 - 1
  // 如果不是 + 1 或 - 1 开头的情况,由于我们是依据操作符进行计算,所以不会造成影响
  const numStack = [0];
  const optStack = [];
  for (let i = 0; i < n; i++) {
    if (s[i] === "(") { // 将左括号加入到栈中,后面进行右括号的清栈计算时,碰到左括号即可弹出
      optStack.push(s[i]);
    } else if (s[i] === ")") {
      // 我们每次碰到右括号都需要进行一次清栈,将与当前右括号匹配的左括号中的式子计算
      iteratorCalc(optStack, calc => {
        // 这里碰到左括号表明计算结束了,可以弹出
        if (optStack[optStack.length - 1] === '(') {
          optStack.pop();
          return false;
        }
        // 这里返回了 calc 是为了满足 iteratorCalc 的要求,calc 表示运算是否成功
        return calc(numStack, optStack)
      });
    } else if (isNum(s[i])) { // 取出当前整个数字
      let num = 0;
      while (i < n && isNum(s[i])) {
        num = num * 10 + +s[i];
        i++;
      }
      i--;
      numStack.push(num);
    } else { // + - * / 符号
      // 这里为什么需要推入个 0 呢?
      // 这是为了方便 calc 计算,如 (+1) || (-1)
      // 我们添个 0 就变为了 (0 + 1) || (0 - 1)
      if (i > 0 && s[i - 1] === '(' && (s[i] === "+" || s[i] === "-")) {
        numStack.push(0);
      }
      // 每当我们遇到一个运算符时,可以进行一次清栈计算
      // 这样如果是一连串相同优先级的运算符的运算,就可以全部计算完成,不会导致栈过大
      iteratorCalc(optStack, (calc, optPrioMap) => {
        let pre = optStack[optStack.length - 1];
        if (optPrioMap[pre] < optPrioMap[s[i]]) return false;
        return calc(numStack, optStack);
      });
      optStack.push(s[i]);  // 我们把推入操作的步骤放在,计算的后面,这是为了先计算前面的部分
    }
  }
  // 最后一次清栈运算,保证所有的运算符都使用了
  iteratorCalc(optStack, calc => calc(numStack, optStack));
  // 栈顶的元素就是我们的结果
  return numStack.pop();
}
全部评论

相关推荐

03-19 18:27
已编辑
门头沟学院 C++
26学院本太难了,很多公司机筛就给我刷了。机会都难拿到如果是简历存在问题也欢迎拷打————————————————————分割线——————————————————————2026.3.4更新:发完贴之后,时不时投递又收到了不少的笔试/面试邀请。主要是之前投递简历出去之后基本上都是沉默状态,年后好转了不少timeline:2026.01.21&nbsp;文远知行笔试,半年多没刷算法题&nbsp;-&gt;挂&nbsp;(后续HR说春招可以重新安排笔试)2026.2.4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;小鹏汇天&nbsp;技术一面,第二周收到结果&nbsp;-&gt;挂2026.2.12&nbsp;&nbsp;&nbsp;大众Cariad代招&nbsp;技术二面&nbsp;-&gt;Offer2026.2.28&nbsp;&nbsp;&nbsp;多益网络技术面试,由于风评太差,一直在犹豫要不要接面试&nbsp;-&gt;推迟-----------分割线-----------2026.3&nbsp;月前的某一天,临时去电网报名了二批计算机岗位的笔试2026.3.6&nbsp;从上家公司实习离职,氛围最好的一家公司,leader&nbsp;说可以帮忙转正,但是流程太长,而且我们部门据说只有一个&nbsp;hc,更想要研究生,我很有可能是会被签外包公司在这里干活,就离职了。2026.3.9&nbsp;入职新公司,大众Cariad&nbsp;以外部公司的身份进组,项目组签了三年,后续三年应该都可以在这里呆,不知道有没有希望原地跳槽。2026.3.10&nbsp;电网考试居然说我通过资格审查了,短信约我去参加资格审查,请假一天,买了&nbsp;12&nbsp;号晚上的机票回成都2026.3.15&nbsp;参加国家电网三新计算机类的笔试2026.3.17&nbsp;电网出成绩了,感觉很低。觉得已经🈚️了2026.3.18&nbsp;收到电网面试通知,通知&nbsp;3.22-3.25&nbsp;这个时间去面试,我的岗位只招&nbsp;1&nbsp;个人。据说面试只有&nbsp;2-3&nbsp;人,不知道能不能成功
点赞 评论 收藏
分享
今天 08:58
已编辑
门头沟学院 Java
ttl:&nbsp;3.19一面晚上过3.20二面3.23oc3.25offerbase:末9有一段中小厂实习一面面经:(总体时长一个小时二十分钟左右没什么八股,主要都是问项目和场景题1.实习(问了有四十分钟,感觉面试官很看重实习这一块,一直在拷打,问到后面我都要疯了,好在准备得比较充分1️⃣用的是什么中间件,有参与技术选型吗,实习的项目里为什么选这个RabbitMQ而不是kafka,为什么不用RocketMQ,为什么放弃异步,自己的项目里面使用的是kafka,那你觉得项目和实习的中间件选型有差异的原因是什么,他们之间的区别在哪里,底层的原因知道吗(高柱到这里已经快疯了,但是硬着头皮答完了,主要是从一致性吞吐量和框架的契合度答,面试官说答得挺好的,应该是没什么问题,这一块就问了快半个小时,到这里我已经快疯了2️⃣项目怎么对接上下游3️⃣介绍项目的难点重点4️⃣微服务(高柱实习是单体项目没涉及这一块5️⃣Redis的使用2.项目:1️⃣智能客服是怎么应用在项目里的(langchain4j➕rag➕functioncalling)2️⃣RAG了解多少3️⃣文本向量化的难点是什么,了解哪些大模型的知识(我一点不懂,纯瞎扯,但貌似扯对了4️⃣对ai的态度是什么,aicoding相关5️⃣怎么保证多节点下Caffeine缓存里面数据都是一致的(答的是短ttl,面试官不是很满意,但是我确实不太懂这个怎么保证,后来查了还是不懂怎么保证6️⃣Redis的使用,和你的实习项目的使用有区别吗,还有一些引申问题3.八股(含量不高,就是走个过场1️⃣进程的内存布局2️⃣Redis三剑客3️⃣微服务相关知识(高柱已经忘得差不多了…勉强答上来4️⃣JVM5️⃣线程状态6️⃣线程安全,在你的实习项目里怎么保证线程安全的(又绕回来了4.智商题找异常球5.手撕:1️⃣五道sql,不难2️⃣力扣不重叠的滑动窗口数组,贪心➕双指针秒了强度拉满了这个一面,高柱到后面人都是傻的二面面经:(就半个小时实习拷打,简历上写了几点就问了几点,问完就结束了,无手撕
查看19道真题和解析
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务