[LeetCode] Basic Calculator I II III 总结

本文详细解析了在LeetCode上实现简易计算器的三道题目,包括基本加减、加减乘除及带括号的复杂表达式计算。通过分解问题,逐一介绍了字符串转数字、加减乘除运算、空格处理及括号处理等关键步骤。

前几天在微信公众号看了一个大佬对于如何实现一个简易计算器的思路分享,学到很多。于是在LeetCode上找到原题码了码,在此记录一下自己的心得。


先放上原题:
LeetCode-224 :Basic Calculator

Implement a basic calculator to evaluate a simple expression string.
The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces .

Example 1:
Input: “1 + 1”
Output: 2

Example 2:
Input: " 2-1 + 2 "
Output: 3

Example 3:
Input: “(1+(4+5+2)-3)+(6+8)”
Output: 23

LeetCode-227 :Basic Calculator II

Implement a basic calculator to evaluate a simple expression string.
The expression string contains only non-negative integers, +, -, *, / operators and empty spaces . The integer division should truncate toward zero.

Example 1:
Input: “3+2*2”
Output: 7

Example 2:
Input: " 3/2 "
Output: 1

Example 3:
Input: " 3+5 / 2 "
Output: 5

LeetCode-772 :Basic Calculator III

Implement a basic calculator to evaluate a simple expression string.
The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces .
The expression string contains only non-negative integers, +, -, *, / operators , open ( and closing parentheses ) and empty spaces . The integer division should truncate toward zero.
You may assume that the given expression is always valid. All intermediate results will be in the range of [-2147483648, 2147483647].

Example 1:
Input: “1 + 1”
Output: 2

Example 2:
Input: " 6-4 / 2 "
Output: 1

Example 3:
Input: “2*(5+5*2)/3+(6/2+8)”
Output: 21

Example 4:
Input: “(2+6* 3+5- (3*14/7+2)*5)+3”
Output: -12


读完这三道题后发现,第三个计算器就是第一个和第二个的汇总,第一道题解决加减的问题,其中包含括号;第二道解决加减乘除问题,不包含括号;第三道题解决加减乘除问题,包含括号。


思路:学会将大问题分解成小模块来一个一个解决。

  1. 将输入的字符串转换成数字
  2. 解决加减乘除问题(不包含括号和空格)
  3. 处理空格问题
  4. 解决括号问题

挨个解决完上述小模块,上面三个问题就都解决了。


字符串转数字模块:
因为输入的是字符串,计算机没法像人一样理解其中的数字,所以得先转化成计算机能够识别的类型。

string s = "256"
int num = 0;
for(int i = 0; i < s.size(); i++)
	{
    	if(isdigit(s[i]))
        	num = num * 10 + (s[i] - '0');
    }

这个模块就能将我们觉得“长的像数字的数字”转化成真正的数字。即num = 256。这里大佬就很细节,在s[i] - ‘0’ 这里加了括号,做了防溢出处理。


加减乘除问题(不包含括号和空格):
根据加减和乘除的运算优先级不同,这里其实可以再细分一下。先看只有加减时的情况。


先附上大佬的代码:

int calculate(string s) {
    stack<int> stk;
    // 记录算式中的数字
    int num = 0;
    // 记录 num 前的符号,初始化为 +
    char sign = '+';
    for (int i = 0; i < s.size(); i++) {
        char c = s[i];
        // 如果是数字,连续读取到 num
        if (isdigit(c)) 
            num = 10 * num + (c - '0');
        // 如果不是数字,就是遇到了下一个符号,
        // 之前的数字和符号就要存进栈中
        if (!isdigit(c) || i == s.size() - 1) {
            switch (sign) {
                case '+':
                    stk.push(num); break;
                case '-':
                    stk.push(-num); break;
            }
            // 更新符号为当前符号,数字清零
            sign = c;
            num = 0;
        }
    }
    // 将栈中所有结果求和就是答案
    int res = 0;
    while (!stk.empty()) {
        res += stk.top();
        stk.pop();
    }
    return res;
}

大佬这里在算加减的时候就直接用栈了,根据每个数前面的符号将数字改成正数或者负数,然后在栈中缓存,最后将栈里的数全部累加。


我觉得这样也可以:

int calculate(string s) {
    int res = 0;
    int num = 0;
    char sign = '+';
    
    for(int i = 0; i < s.size(); i++)
    {
        if(isdigit(s[i]))
            num = num * 10 + (s[i] - '0');

        if(!isdigit(s[i]) || i == s.size() - 1)
        {
            switch(sign)
            {
                case '+':
                    res += num;
                    break;
                case '-':
                    res -= num;
                    break;
            }
            sign = s[i];
            num = 0;
        }
    }
    return res;
}

因为题中说了没有负数,所以可以默认给给第一个数正号。这里和大佬得区别是暂时没有用栈,因为只有加减,可以从左到右按顺序直接运算,所以在switch语句里我就根据符号直接做运算了,而没有缓存。

再来看看加入乘除运算后应该做哪些改动。先附上代码:

class Solution {
public:
    int calculate(string s) {
        stack<int> nums;
        int num = 0;
        int res = 0;
        char sign = '+';
        for(int i = 0; i < s.size(); i++){
            if(isdigit(s[i]))
                num = num * 10 + (s[i] - '0');
            
            if(!isdigit(s[i]) || i == s.size() - 1){
                int temp = 0;
                switch(sign){
                    case '+':
                        nums.push(num);
                        break;
                    case '-':
                        nums.push(-num);
                        break;
                    case '*':
                        temp = nums.top() * num;
                        nums.pop();
                        nums.push(temp);
                        break;
                    case '/':
                        temp = nums.top() / num;
                        nums.pop();
                        nums.push(temp);
                        break;
                }
                sign = s[i];
                num = 0;
            }
        }
        while(!nums.empty()){
            res += nums.top();
            nums.pop();
        }
        
        return res;
    }
};

加了乘除之后,我妥协了。还是用栈好啊!!!当然,我觉得这里其实也可以不用栈,毕竟只是需要对乘/除法运算的第一个乘/除数做缓存。可以用数组,甚至只需要多一个变量。但是用栈的话操作方便啊,代码也更简洁了。


解决空格问题:
因为这里用了!isdigit(s[i]),所以空格会影响这里的if判决,改成如下的条件即可:

 if((!isdigit(s[i]) && s[i] != ' ') || i == s.size() - 1)

或者:

 if((!isdigit(s[i]) && !isspace(s[i])) || i == s.size() - 1)

这样就会直接跳过空格了。目前为止,第二道题已经做出来了。


解决括号问题:
先放出我第一道题的代码,再分享思路。

int calculate(string s) {
    int res = 0;
    stack<int> nums;
    stack<char> signs;
    int num = 0;
    char sign = '+';
    
    for(int i = 0; i < s.size(); i++)
    {
        if(isdigit(s[i]))
            num = num * 10 + (s[i] - '0');
        
        if(s[i] == '(')
        {
            nums.push(res);
            signs.push(sign);
            res = 0;
            sign = '+';
        }
        else if(s[i] == ')')
        {
            switch(sign)
            {
                case '+':
                    res += num;
                    break;
                case '-':
                    res -= num;
                    break;
            }
            num = 0;
            switch(signs.top())
            {
                case '+':
                    res = nums.top() + res;
                    break;
                case '-':
                    res = nums.top() - res;
                    break;
            }
            nums.pop();
            signs.pop();
        }
        else if((!isdigit(s[i]) && s[i] != ' ') || i == s.size() - 1)
        {
            switch(sign)
            {
                case '+':
                    res += num;
                    break;
                case '-':
                    res -= num;
                    break;
            }
            sign = s[i];
            num = 0;
        }
    }
    
    return res;
}

大佬对于括号的理解是递归,一开始不理解,看见下面这张图恍然大悟。
在这里插入图片描述
思想我是知道了,但是由于水平有限,一直写不出递归代码。但递归不就是通过栈实现的嘛!!!所以索性我就改为用栈来实现:只要一遇到左括号,就将之前的结果和符号入栈,接着运算括号内的表达式,思路和之前一样,直到遇到右括号,将括号内得到的结果和数栈顶的结果根据符号栈顶的符号相运算。于是有了第一题答案。


试了一早上,没把这两道题的代码合成第三道题的代码,我觉得可能是因为我做这两道题用的思路不一样吧。所以在网上找了找代码,终于能统一思路了,也学会了怎么在这道题上运用递归的思想。感谢“labuladong”大佬,感谢“无差别刷题”大佬。

class Solution {
public:
    int calculate(string s) {
        stack<int> st;
        long num = 0; 
        int result = 0;
        int op ='+';
        
        for (int i = 0; i < s.size(); i++) {
            char c = s[i];
            if (isdigit(c)) {
                num = num * 10 + c - '0';
            }
            else if (c == '(') {
                int j = i, count = 0;
                for (;i < s.size(); i++) {
                    if (s[i] == '(') count ++;
                    if (s[i] == ')') count --;
                    if (count == 0) break;
                }
                num = calculate(s.substr(j + 1, i - j - 1));
            }
            if (!isdigit(c) && !isspace(c) || i == s.size() - 1){
                if (op == '+') st.push(num);
                if (op == '-') st.push(-num);
                if (op == '*' || op == '/') {
                    int top = st.top();
                    st.pop();
                    if (op == '*') st.push(top * num);
                    if (op == '/') st.push(top / num);
                }
                op = s[i];
                num = 0;
            }
        }
        
        while (!st.empty()) {
            result += st.top();
            st.pop();
        }
        return result;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值