用JavaScript刷leetcdoe第227题-基本计算器Ⅱ(双栈法)

156 阅读1分钟

一、前言

本来是准备用分治递归来解的,但是显示超时。超时代码如下,然后就改用双栈来解这道题了。双栈或者分治递归属于这类题的常规解法

/**
 * @param {string} s
 * @return {number}
 */
var calculate = function(s) {
  return calc(s, 0, s.length - 1);
}

function calc(s, l, r) {
  // pos:记录优先级最低运算符位置 pri: 记录优先级最低运算符的优先级 curPri: 当前运算符的优先级
  let pos = -1, pri = 10000 -1, curPri
  for(let i = l; i < r + 1; i++) {
    curPri = 10000

    switch(s[i]) {
      case '+': 
      case '-': curPri = 1; break;
      case '*': 
      case '/': curPri = 2; break;
    }

    if(pri >= curPri) {
      pos = i;
      pri = curPri;
    }
    
  }

  if(pos === -1) {
    let num = 0;

    for(let i = l; i < r + 1; i++) {
      if(/0-9/.test(s[i])) num = num * 10 + (+s[i]);
    }

    return num;
  }

  const a = calc(s, l, pos - 1);
  const b = calc(s, pos + 1, r);

  switch(s[pos]) {
    case '+': return a + b;
    case '-': return a - b;
    case '*': return a * b;
    case '/': return a / b;
  }

}

二、题目描述

原谅我比较懒,我直接从leetcode截图,欲知更加清楚的描述,请看leetcode题目链接

image.png

三、大致思路

  • 双栈:操作符栈、操作数栈
  • 从左向右扫描表达式
  • 操作数直接入栈
  • 操作符需要比较当前操作符和操作符栈内操作符的优先级
  • 操作符栈 中 比 当前操作符优先级低的操作符, 计算并将计算结果放入操作数栈中

四、具体代码

注释比较详细,git代码链接

/**
 * @param {string} s
 * @return {number}
 */
var calculate = function(s) {
  // 补一个优先级最低的操作符,会直接清空操作符栈,得到最终结果。
  s+='@';
  // 操作数栈
  const numStack = [];
  // 操作符栈
  const opsStack = [];
  // 遍历s
  for(let i = 0, n = 0; i < s.length; i++) {
    // 空白符不处理
    if(/\s/.test(s[i])) continue;
    // 处理数字,举个例子 遇见'123'
    if(/[0-9]/.test(s[i])) {
      n = n * 10 + (+s[i]);
      continue;
    }

    // ********************* 代码到了这里说明当前字符是操作符 ***************************

    // 将数字压栈
    numStack.push(n);
    // 初始化数字
    n = 0;
    // 找出 操作符栈 中 比 当前操作符优先级低的操作符, 计算并将计算结果放入操作数栈中
    while(opsStack.length && level(opsStack[opsStack.length - 1]) >= level(s[i])) {
      const b = numStack.pop();
      const a = numStack.pop();
      const ops = opsStack.pop();
      const num = calc(a, b, ops);
      numStack.push(num);
    }
    // 当前操作符压栈
    opsStack.push(s[i]);
  }
  // 由于最后一个操作符是@优先级最低,所以相当于我们已经清空了操作符栈,直接返回操作数栈中的栈顶元素即是计算结果
  return numStack.pop();
};

/**
 * @params 操作符
 * @return 操作符对应的优先级
 */
function level(ops) {
  switch(ops) {
    case '+':
    case '-': return 1
    case '*':
    case '/': return 2
    case '@': return -1
  }
}

/**
 * @params 数字,数字,操作符
 * @return 计算后得到的值
 */
function calc(a,b,ops) {
  switch(ops) {
    case '+': return a + b;
    case '-': return a - b;
    case '*': return a * b;
    case '/': return Math.floor(a / b);
  }
}