【前端也得会算法】224. 基本计算器 [ 困难 ]

228 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

一、题目描述:

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

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

示例

示例 1:
输入: s = "1 + 1"
输出: 2

示例 2:
输入: s = " 2-1 + 2 "
输出: 3

示例 3:
输入: s = "(1+(4+5+2)-3)+(6+8)"
输出: 23

提示:

  • 1 <= s.length <= 3 * 105
  • s 由数字、'+'、'-'、'('、')'、和 ' ' 组成
  • s 表示一个有效的表达式
  • '+' 不能用作一元运算(例如, "+1" 和 "+(2 + 3)" 无效)
  • '-' 可以用作一元运算(即 "-1" 和 "-(2 + 3)" 是有效的)
  • 输入中不存在两个连续的操作符`
  • 每个数字和运行的计算将适合于一个有符号的 32位 整数

二、题解:

方法一 符号记录法

  • 原理。使用字符串拼接数字,number转化类型,记录打开括号后所有的符号和数字即可。
  • 思路。
    • 使用replaceAll替换所有空格
    • 使用字符串拼接数字,并判断是否是负数
    • 遇到'('括号使用数组记录括号前的符号(正为1,负为-1),下一个符号需要依赖于数组最后一个的符号
    • 遇到')'括号则数组退出一个

代码:

var calculate = function (s) {
    if (Number(s) === parseInt(s)) {
        return Number(s)
    }
    s = s.replaceAll(' ', '')
    let i = 0;
    let sum = 0
    let stackChat = [1]
    let tempStr = ''
    while (i < s.length) {
        if (s[i] === '' || s[i] === '+' || s[i] === '-') {
            sum += Number(tempStr)
            tempStr = ''
            i++
        } else if (s[i] === '(') {
            if (i - 1 >= 0 && s[i - 1] === '+') {
                stackChat.push(stackChat[stackChat.length - 1])
            } else if (i - 1 >= 0 && s[i - 1] === '-') {
                stackChat.push(-stackChat[stackChat.length - 1])
            } else {
                stackChat.push(1)
            }
            i++
        } else if (s[i] === ')') {
            stackChat.pop()
            i++
        } else {
            if (stackChat.length>1) {
                let lastNum = 1
                if (i - 1 >= 0 && s[i - 1] === '-') {
                    lastNum = -1
                }
                let _stackChat = stackChat[stackChat.length - 1]
                tempStr += (_stackChat * lastNum === -1 && !tempStr ? '-' : '') + s[i]

            } else {
                tempStr += (i - 1 >= 0 && s[i - 1] === '-' ? '-' : '') + s[i]
            }
            i++
        }
    }
    sum += Number(tempStr)
    return sum
};

image.png

方法二 符号变化法

  • 原理。逐步进行,符号记录后转化为正确的符号,并使用循环叠加方式计算2位数以上的数字。

代码:

var calculate = function(s) {
    if(Number(s) === parseInt(s)){
        return Number(s)
    }
    let i = 0;
    let ops = [1]
    let sign = 1
    let sum = 0
    while (i < s.length) {
        let item = s[i]
        if (item === ' ') {
            i++
        } else if (item === '+') {
            sign = ops[ops.length - 1]
            i++
        } else if (item === '-') {
            sign = -ops[ops.length - 1]
            i++
        } else if (item === '(') {
            ops.push(sign)
            i++
        } else if (item === ')') {
            ops.pop()
            i++
        } else {
            let num = 0
            while (!isNaN(Number(s[i])) && i < s.length && s[i] !== ' ') {
                num = num *10 + Number(s[i])
                i++
            }
            sum+= sign*num
        }
    }
    return sum
};

image.png

方案比较

方案一 时间复杂度O(N) 方案二 时间复杂度O(N),虽然是双循环,但是i共用,所以没有多余的时间开销

三、总结

  • 此题可以符号记录法符号变化法两种方案
  • 逐步比较法主要是使用字符串拼接数字,number转化类型,记录打开括号后所有的符号和数字即可。
  • 记录缺陷法逐步进行,符号记录后转化为正确的符号,并使用循环叠加方式计算2位数以上的数字。

文中如有错误,欢迎在评论区指正