LeetCode - 224. Basic Calculator

LeetCode - 224. Basic Calculator

在算法学习的漫漫长路中,我们总会遇到一些颇具挑战性的问题,而 LeetCode 上的第 224 题 —— 基本计算器(Basic Calculator)便是其中之一。这道题要求我们根据给定的算术表达式字符串,计算出其结果,字符串中包含 ‘+’、‘-’、’ ‘、’(‘、’)’ 这些元素。虽然题目看似简单,但其背后涉及的字符串解析与计算逻辑,足以考验我们对算法和编程技巧的掌握程度。今天,就让我们一同深入剖析这道题,并通过 JavaScript 代码来实现它的解法。

1. 问题深度剖析

1.1 输入与输出的明确界定

我们的输入是一个算术表达式字符串。这个字符串可能是简单的 “1 + 1”,也可能是复杂的 “(1 + (4 + 5 + 2) - 3) + (6 + 8)”。无论其形式如何,我们都需要从这个字符串中提取有效的信息,并按照数学运算规则进行计算。而输出,则是这个算术表达式计算后的结果,对于前面提到的两个例子,输出分别为 2 和 23。

1.2 关键挑战点解析

这道题的核心挑战在于如何准确解析字符串,处理其中的空格、运算符、数字以及括号。其中,括号的存在极大地增加了题目的复杂性,因为它改变了运算的优先级,我们需要优先计算括号内的表达式。

2. 解题思路的逐步拆解

2.1 关键要点提炼

解决这个问题的关键在于两个方面:一是精确解析字符串,将其转化为计算机能够理解和处理的数据结构;二是设计合理的算法来计算结果,特别是要处理好括号对运算顺序的影响。

2.2 具体解题步骤

  • a. 解析字符串:我们首先要将输入的字符串解析成一个便于处理的数据结构。在我们的 JavaScript 代码中,通过pareToArray函数来完成这一任务。这个函数会遍历输入字符串,将数字、运算符和括号分别提取出来,并处理可能存在的前导符号。例如,如果字符串以 ‘-’ 或 ‘+’ 开头,函数会在前面补 0,以便后续统一处理。在遍历过程中,若遇到数字字符,会判断其前一个元素是否也是数字字符,如果是,则将当前数字字符追加到前一个数字字符串上,从而正确处理多位数的情况。

  • b. 计算括号内结果:在解析完字符串后,我们使用栈来处理表达式。当遇到 ‘)’ 时,我们知道需要开始计算括号内的结果了。此时,我们会从栈中取出相关元素,进行计算,直到遇到对应的 ‘(’。在 JavaScript 代码中,这一过程由calculateWithoutBracket函数实现。该函数从栈中弹出元素,根据元素的类型(是数字、加号标志还是减号标志)进行相应的运算,计算出括号内的临时结果。

  • c. 计算最终结果:在处理完所有括号内的表达式后,我们继续处理栈中剩余的元素,最终得到整个表达式的计算结果。这一过程同样在calculate函数中完成,通过循环遍历解析后的数组,将元素依次压入栈中,并根据遇到的 ‘(’ 触发括号内的计算操作,最后栈顶元素即为最终的计算结果。

3. JavaScript 代码实现的详细解读

var calculate = function (s) {
    if (s.length === 0) {
        return 0;
    }

    let arr = pareToArray(s);

    let stack = [];
    for (let i = arr.length - 1; i >= 0; i--) {
        if (arr[i] !== '(') {
            if (arr[i] === '-') {
                stack.push(false);
            } else if (arr[i] === '+') {
                stack.push(true)
            } else if (arr[i] === ')') {
                stack.push(arr[i])
            } else if (arr[i] !== ' ') {
                stack.push(Number(arr[i]));
            }
        } else {
            calculateWithoutBracket(stack);
        }
    }
    calculateWithoutBracket(stack);
    return stack.pop();
};

var pareToArray = function (s) {
    if (s[0] === '-' || s[0] === '+') {
        s = 0 + s;
    }

    let arr = [];
    for (let i = 0; i < s.length; i++) {
        if (s[i] === ' ') {
            continue;
        }
        if (isNaN(Number(s[i]))) {
            arr.push(s[i]);
        } else {
            if (isNaN(Number(arr[arr.length - 1]))) {
                arr.push(s[i]);
            } else {
                arr[arr.length - 1] += s[i];
            }
        }
    }
    return arr;
}

var calculateWithoutBracket = function (stack) {
    let tempSum = stack.pop();
    while (stack.length !== 0 && stack[stack.length - 1] !== ')') {
        let item = stack.pop();
        if (item === false) {
            tempSum -= stack.pop();
        } else {
            tempSum += stack.pop();
        }
    }

    if (stack[stack.length - 1] === ')') {
        stack.pop();
    }

    stack.push(tempSum);
}

3.1 calculate函数

这个函数是整个程序的入口。首先,它会检查输入字符串是否为空,如果为空则直接返回 0。接着,调用pareToArray函数将字符串解析为数组arr。然后,初始化一个栈stack,从数组arr的末尾开始向前遍历。在遍历过程中,根据元素的类型进行不同的操作:如果是数字,则将其转换为数字类型后压入栈;如果是 ‘+’,则压入true表示加号;如果是 ‘-’,则压入false表示减号;如果是 ‘)’,则直接压入栈。当遇到 ‘(’ 时,调用calculateWithoutBracket函数计算括号内的结果。最后,再次调用calculateWithoutBracket函数处理栈中剩余的元素,并返回栈顶元素,即最终的计算结果。

3.2 pareToArray函数

该函数负责将输入字符串解析为数组。如果字符串以 ‘-’ 或 ‘+’ 开头,会在前面补 0。然后遍历字符串,跳过空格字符。对于非数字字符,直接将其添加到数组中;对于数字字符,会判断数组中前一个元素是否也是数字,如果是,则将当前数字字符追加到前一个数字字符串上,最终返回解析后的数组。

3.3 calculateWithoutBracket函数

这个函数用于计算括号内的结果。首先从栈中弹出一个元素作为临时和tempSum。然后进入循环,只要栈不为空且栈顶元素不是 ‘)’,就继续从栈中弹出元素。如果弹出的元素是false(表示减号),则从栈中再弹出一个元素并从tempSum中减去;如果弹出的元素是true(表示加号),则从栈中再弹出一个元素并加到tempSum上。当循环结束后,如果栈顶元素是 ‘)’,则将其弹出,最后将计算得到的tempSum压入栈中。

4. 代码的优化思考与展望

虽然当前的代码已经能够正确解决基本计算器问题,但仍有一些可以优化的地方。例如,在解析字符串部分,可以考虑使用更高效的字符串处理方法,减少不必要的判断和操作。在计算过程中,栈的使用可以进一步优化,以减少空间复杂度。另外,对于边界情况和异常输入的处理还可以更加完善,比如处理非法的表达式格式等。

通过对 LeetCode 224 题的分析与解答,我们不仅掌握了如何解决一个复杂的算术表达式计算问题,更重要的是,在这个过程中提升了对字符串解析、栈数据结构以及算法设计的理解和运用能力。希望这篇博客能够为正在学习算法的你提供一些帮助和启发,让我们一起在算法的世界中不断探索前行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿蒙Armon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值