【路飞】-算法练习——基础计算器 II

179 阅读1分钟

记录一次算法优化的历程

227. 基本计算器 II

【题目:】 给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

示例 1:

输入:s = "3+2*2"
输出:7

示例 2:

输入:s = " 3/2 "
输出:1

示例 3:

输入:s = " 3+5 / 2 "
输出:5

提示:

  • 1 <= s.length <= 3 * 105
  • s 由整数和算符 ('+', '-', '*', '/') 组成,中间由一些空格隔开
  • s 表示一个 有效表达式
  • 表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
  • 题目数据保证答案是一个 32-bit 整数

题解:

  • s一定是一个 有效表达式
  • 使用两个数组retArrstack来存储表达式中的数字和符号,注意s中是字符串,需要找出看起来是数字的字符串,并存储在retArr

在起初的解法中,我是先将数字和运算符选出,再装进一个数组里,之后循环计算乘除,最后再去计算总和,这样就多做了一些循环操作,结果就是运算时间过长

旧代码:

/**
 * @param {string} s
 * @return {number}
 */
 var calculate = function(s) {
  let l = s.trim()
  let num =[],stack = [], symbolArr = ['+','-','*','/'],nums = []
  for(let i = 0; i< l.trim().length; i++) {
      if(l[i]===' ') {
          continue
      }
      if(!symbolArr.includes(l[i])){
          num.push(l[i])
          num = [Number(num.join(''))]
      }else{
          stack.push(l[i])
          let n = Number(num.join(''))
          num = []
          nums.push(n)
      }
      if(i===l.length-1) {
          if(num.length === 1) {
              nums.push(Number(num[0]))
          }else{

              nums.push(Number(l[i]))
          }
      }
  }

  let retArr = []
  for(let k = 0; k < stack.length; k++){
      retArr.push(nums[k])
      retArr.push(stack[k])
  }
  retArr.push(nums[nums.length-1])


  let stack2 = []
  let first = retArr.shift()
  stack2.push(first)
  while(retArr.length){
      let symbol = retArr.shift()
      let second = retArr.shift()
      if(symbol === '*'){
          stack2.push(stack2.pop()*second)
          
      }else
      if(symbol === '/'){
          stack2.push(parseInt(stack2.pop()/second))
      }else
      if(symbol === '+'){
          stack2.push(second)
      }else
      if(symbol === '-'){
          stack2.push(-second)
      }
      
  }
  let ret = splice(stack2)
  return ret
}

function splice(nums) {
  let ret = 0
  if(nums.length === 1) return nums[0]
  if(nums.length === 2) return nums[0] + nums[1]
  if (nums.length > 2) {
    
    let left = nums.splice(0, nums.length/2)
    let right = nums
    let retLeft = splice(left)
    let retRight = splice(right)
    ret = retLeft + retRight
    return ret
  }
}

对于计算长达209079的有效字符串,运行时间长达近7s image.png

为解决过多循环问题,可以优化循环计算的时机:

  • 循环s的过程中,‘*’‘/’ 需要优先计算,做减法的数字看作为负数,retArr只存储需要做加法的数字

优化后代码:

/**
 * @param {string} s
 * @return {number}
 */
var calculate = function (s) {
  let l = s.trim()
  let num = '', stack = [], symbolArr = ['+', '-', '*', '/'], retArr = []
  for (let i = 0; i < l.trim().length; i++) {
    if (l[i] === ' ') {
      continue
    }
    if (!symbolArr.includes(l[i])) {
      num += l[i]
      if (i === l.length - 1) {
        let n = Number(num)
        retArr.push(n)
        num = ''
        while (stack.length) {

          retArr = cacl(stack, retArr)
        }
      }

    } else {
      let n = Number(num)
      retArr.push(n)

      while (stack.length) {

        retArr = cacl(stack, retArr)
      }

      stack.push(l[i])
      num = ''

    }
  }
  return retArr.reduce((a, b) => a + b, 0)
}
function cacl(stack, retArr) {
  let right = retArr.pop()
  let left = retArr.pop()
  let symbor = stack[stack.length - 1]
  let ret = 0
  // 计算
  switch (symbor) {
    case "*":
      ret = left * right
      break
    case '/':
      ret = parseInt(left / right)
      break
    case '+':
      retArr.push(left)
      ret = right
      break
    case '-':
      retArr.push(left)
      ret = -right
      break
  }
  stack.pop()
  retArr.push(ret)
  return retArr
}

image.png