224.基本计算器

题目:
实现一个基本的计算器来计算一个简单的字符串表达式 s 的值。

示例 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 表示一个有效的表达式

题目链接:
https://leetcode-cn.com/problems/basic-calculator

思路
计算器的题目基本都和栈有关,这道题也不例外。
由题目信息可知,s 中一共包含以下几种数据:
空格
数字
操作符。这里有 + - * /
而对于操作符来说又可以进一步细分:
一元操作符 + -
二元操作符 * /
对于一元操作符来说,我们只需要知道一个操作数即可。这个操作数就是操作符右边的数字。为了达到这个效果,我们需要一点小小的 trick。
1 + 2
我们可以在前面补充一个 + 号,变成:

  • 1 + 2

可看成

(+1)(+2)
再比如:
(-1)(+2)(+3)(-4)
括号只是逻辑分组,实际并不存在。下同,不再赘述。
而对于二元操作符来说,我们需要知道两个操作数,这两个操作数分别是操作符两侧的两个数字。
(5) / (2)
再比如
(3) * (4)
简单来说就是,一元操作符绑定一个操作数。而二元操作符绑定两个操作数。

算法:
从左到右遍历 s
如果是数字,则更新数字
如果是空格,则跳过
如果是运算符,则按照运算符规则计算,并将计算结果重新入栈,具体见代码。最后更新 pre_flag 即可。
为了简化判断, 我使用了两个哨兵。一个是 s 末尾的 $,另一个是最开始的 pre_flag。

关键点解析
记录 pre_flag,即上一次出现的操作符
使用哨兵简化操作。一个是 s 的 $ ,另一个是 pre_flag 的 +
代码

class Solution:
    def calculate(self, s: str) -> int:
        stack = []
        s += '$'
        pre_flag = '+'
        num = 0

        for c in s:
            if c.isdigit():
                num = num * 10 + int(c)
            elif c == ' ': continue
            else:
                if pre_flag == '+':
                    stack.append(num)
                elif pre_flag == '-':
                    stack.append(-num)
                elif pre_flag == '*':
                    stack.append(stack.pop() * num)
                elif pre_flag == '/':
                    stack.append(int(stack.pop() / num))
                pre_flag = c
                num = 0
        return sum(stack)

复杂度分析
时间复杂度:O(N)O(N)
空间复杂度:O(N)O(N)

扩展
基本计算器 和这道题差不多,官方难度困难。就是多了个括号而已。所以基本上可以看做是这道题的扩展。题目描述:
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。

示例 1:
输入: “1 + 1”
输出: 2

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

示例 3:
输入: “(1+(4+5+2)-3)+(6+8)”
输出: 23

说明:
你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。
拿题目中最难的例子来说 “(1+(4+5+2)-3)+(6+8)”。我们可以将其拆分为:

6+8 (= 14)
4 + 5 + 2 (=11)
(11) - 3 (=8)
1 + (8) (=9)
9 + (14) (=23)
简单来说就是将括号里面的内容提取出来,提取出来就是上面的问题了。用上面的方法计算出结果,然后将结果作为一个数字替换原来的表达式。

比如我们先按照上面的算法计算出 6 + 8 的结果是 14,然后将 14 替换原来的 (6+8),那么原问题就转化为了(1+(4+5+2)-3)+14 。这样一步一步就可以得到答案。

因此我们可以使用递归,每次遇到 ( 则开启一轮新的递归,遇到 )则退出一层递归即可。

Python 代码:

class Solution:
    def calculate(self, s: str) -> int:
        def dfs(s, start):
            stack = []
            pre_flag = '+'
            num = 0
            i = start
            while i < len(s):
                c = s[i]
                if  c == ' ':
                    i += 1
                    continue
                elif c == '(':
                    i, num = dfs(s, i+1)
                elif c.isdigit():
                    num = num * 10 + int(c)
                else:
                    if pre_flag == '+':
                        stack.append(num)
                    elif pre_flag == '-':
                        stack.append(-num)
                    if c == ')': break
                    pre_flag = c
                    num = 0
                i += 1
            return i, sum(stack)
        s += '$'
        return dfs(s, 0)[1]

作者:fe-lucifer
链接:https://leetcode-cn.com/problems/basic-calculator/solution/li-kou-jia-jia-zhan-de-ying-yong-224-ji-10rkt/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

解题思路:
重点:
本题目只有 “+”, “-” 运算,没有 “*” , “/” 运算,因此少了不同运算符优先级的比较;
遇到小括号,应该先算括号里面的表达式;
递归
一个表达式分为三部分:
左边表达式①,运算符③, 右边表达式②
左边表达式①,运算符③,右边表达式②

本题中,左边和右边的表达式可以是一个数字,也可以是一个括号包起来的表达式;运算符可以是加减。
小学数学告诉我们,一个只包含加减和括号的表达式,我们可以从左到右计算,遇到括号就先算括号里面的。具体来说就是先计算左边的表达式,再计算右边表达式,最后根据运算符,计算 ①和②的运算 。
用题目示例 “(1+(4+5+2)-3)+(6+8)” 来说明运算符计算的顺序:
在这里插入图片描述
根据上面的分析可知,当我们在计算一个表达式的时候,需要先计算左边表达式①,然后需要把①的结果和运算符③保存起来,再需要计算右边表达式②,最后计算①和②的运算。这个操作就是递归!!


递归的程序可以用「栈」来模拟:栈为了保存左边表达式①的计算结果和运算符③,在计算右边表达式③的结果之后,从栈中取出运算符③和①的结果,再进行计算整个表达式的结果。

肯定有朋友想问了,用栈保存左边表达式结果的话,当遇到嵌套的括号怎么办?比如 (1 + (2 + (3 + 4)))。答案是:栈顶保留的是最里层嵌套的运算,弹出栈的时候,正好先算的是最里面括号的,再算外边括号的。这种情况时,栈里面保存的是 [“1”, “+”, “2”, “+”, “3”, “+”],然后遇到 4,此时计算的是 3 + 4,然后算 7 + 2,再算 9 + 1。可以通过递归来帮助理解。

代码
代码里面:

res 表示左边表达式除去栈内保存元素的计算结果;
sign 表示运算符;
num 表示当前遇到的数字,会更新到 res 中;
用栈保存遇到左括号时前面计算好了的结果和运算符。
操作的步骤是:

如果当前是数字,那么更新计算当前数字;
如果当前是操作符+或者-,那么需要更新计算当前计算的结果 res,并把当前数字 num 设为 0,sign 设为正负,重新开始;
如果当前是 ( ,那么说明遇到了右边的表达式,而后面的小括号里的内容需要优先计算,所以要把 res,sign 进栈,更新 res 和 sign 为新的开始;
如果当前是 ) ,那么说明右边的表达式结束,即当前括号里的内容已经计算完毕,所以要把之前的结果出栈,然后计算整个式子的结果;
最后,当所有数字结束的时候,需要把最后的一个 num 也更新到 res 中。
在这里插入图片描述
作者:fuxuemingzhu
链接:https://leetcode-cn.com/problems/basic-calculator/solution/ru-he-xiang-dao-yong-zhan-si-lu-lai-zi-y-gpca/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

代码:

class Solution(object):
    def calculate(self, s):
        res, num, sign = 0, 0, 1
        stack = []
        for c in s:
            if c.isdigit():
                num = 10 * num + int(c)
            elif c == "+" or c == "-":
                res += sign * num
                num = 0
                sign = 1 if c == "+" else -1
            elif c == "(":
                stack.append(res)
                stack.append(sign)
                res = 0
                sign = 1
            elif c == ")":
                res += sign * num
                num = 0
                res *= stack.pop()
                res += stack.pop()
        res += sign * num
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Xamarin.Forms 是一个跨平台开发框架,可用于开发运行在 iOS、Android 和 UWP 等多个平台的应用程序。所以在使用 Xamarin.Forms 开发计算器应用时,我们需要处理计算器的符号问题。 在计算器应用中,常用的计算符号有加法、减法、乘法和除法。我们可以使用 Xamarin.Forms 中的按钮控件来表示这些符号,并为这些按钮添加相应的点击事件处理逻辑。 首先,我们可以创建四个按钮来表示加法、减法、乘法和除法符号。例如,我们可以创建一个按钮来表示加法符号“+”,并在按钮的 Clicked 事件回调中执行加法运算的逻辑。 ```csharp var addButton = new Button { Text = "+" }; addButton.Clicked += (sender, e) => { // 执行加法运算的逻辑 // ... }; ``` 类似地,我们可以为减法、乘法和除法符号分别创建对应的按钮,并为它们的 Clicked 事件回调添加相应的逻辑。 在具体的运算逻辑中,我们可以使用 C# 中的数学运算符来执行相应的计算操作。例如,利用加法运算符进行加法运算,利用减法运算符进行减法运算,以此类推。 通过在 Xamarin.Forms 中创建按钮来表示计算器符号,并在相应的点击事件回调中执行相应的运算逻辑,我们可以实现一个简单的计算器应用,用于处理不同符号的计算操作。 ### 回答2: Xamarin.Forms是一个用于创建跨平台移动应用的工具集。对于计算器符号,我们可以结合Xamarin.Forms的特性和功能来实现。首先,我们可以使用XAML来创建界面布局,然后在代码中添加功能。 对于计算器符号,我们可以使用各种方式来实现。一种常见的方式是使用字体图标库,如FontAwesome或Material Design Icons。这些库提供了各种各样的符号图标,可以通过在XAML中引用设置图标。 另一种方式是使用图片作为符号。我们可以选择符号图片并将其包含在我们的项目中。然后,我们可以在XAML中使用Image控件来显示这些图片符号。 同时,我们还可以使用字符来表示符号。Xamarin.Forms允许在文本控件中使用Unicode字符。我们可以在XAML或代码中直接设置控件的文本属性并使用特定的Unicode字符表示计算器符号。 综上所述,对于Xamarin.Forms的计算器符号,我们可以通过使用字体图标库、图片或Unicode字符来实现。这取决于我们的需求和喜好。无论我们选择哪种方式,Xamarin.Forms都提供了丰富的功能和灵活的选项,使我们能够轻松实现并使用计算器符号。 ### 回答3: 在Xamarin.Forms中实现计算器符号的方法如下: 1. 创建一个Xamarin.Forms项目,选择适当的模板作为起点。 2. 在XAML中设计计算器的界面,包括数字按钮、操作符按钮和结果显示框。 3. 创建一个ViewModel类来处理计算逻辑。可以使用Command模式来处理按钮的点击事件和计算逻辑。 4. 在ViewModel中,使用逻辑运算符和条件语句来实现计算器的功能。例如,可以使用if语句判断输入的操作符是加法、减法、乘法还是除法,并根据不同的操作符执行相应的计算操作。 5. 使用绑定机制将计算结果显示在界面上。可以将结果显示框绑定到ViewModel中的一个属性,当计算结果发生变化时,界面会自动更新。 6. 测试计算器的功能,确保各种操作符都能正确计算结果。 综上所述,通过在Xamarin.Forms中使用ViewModel和绑定机制,我们可以实现一个简单的计算器,可以处理各种运算符号,并将结果展示在界面上。以上方法只是其中一种实现方式,您也可以根据自己的需要和喜好进行调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值