记录一次算法优化的历程
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一定是一个 有效表达式
- 使用两个数组retArr和stack来存储表达式中的数字和符号,注意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
为解决过多循环问题,可以优化循环计算的时机:
- 循环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
}