【小白爬Leetcode224】2.5 基本计算器 Basic Calculator


Leetcode 224 h a r d \color{#FF0000}{hard} hard

点击进入原题链接:Leetcode224 基本计算器 Basic Calculator

题目

Description

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 .
在这里插入图片描述
Note:

  1. You may assume that the given expression is always valid.
  2. Do not use the eval built-in library function.

中文描述

实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。
在这里插入图片描述说明:

  1. 你可以假设所给定的表达式都是有效的。
  2. 请不要使用内置的库函数 eval。

思路一 状态机(正序读取表达式)

用两个栈num_stack用来存放操作数字,以及operator_stack用来存放操作符(+\-)
读取字符串的时候,设置一个STATE状态变量:

  1. 当读取到数字时,STATE=NUM_STATE; (注意用于switch判断的判断值应该是const型)
  2. 读取到空格直接跳过本次循环(注意在continue之前要有一个字符串指针后移操作i++表示已经读取过这个空格,否则会出现死循环)
  3. 读取到其他符号(( )(+\-))时,STATE=OPERATOR_STATE

接下来用switch来判断STATE的状态,进入不同的分支。
CAL_flag来记录是否可以进行运算。遇到操作符(+\-)和右括号')'时候,CAL_flag=1,表示遇到下一个数就要进行相应的计算操作,并将结果push进num_stack中;遇到左括号'('时,CAL_flag=0,表示目前不进行运算(而是运算括号内的值)。

class Solution {
public:
	//执行计算的函数
	void compute(std::stack<int>& num_stack, std::stack<char>& operator_stack) {
		if (num_stack.size() < 2) return; //处理"2147483647"这样的特殊情况
		char opt = operator_stack.top(); //操作符
		int num1 = num_stack.top();  //操作数1
		num_stack.pop();
		int num2 = num_stack.top();  //操作数2
		num_stack.pop();
		operator_stack.pop();
		if (opt == '+') {
			num_stack.push(num2 + num1);
		}
		else if (opt == '-') num_stack.push(num2 - num1);  //特别要注意,由于栈的特性,减数是num1,而被减数反而成了num2。
	}
	//主体函数
	int calculate(string s) {
		const int NUM_STATE = 0;  //注意用于switch判断的值要是const型
		const int OPERATOR_STATE = 1;
		int STATE;
		int CAL_flag = 0;
		int len = s.size();
		char cur;
		int i=0;
		int num;
		while (i < len) {  //正向遍历字符串
			cur = s[i];
			if (cur == ' ') {i++;continue;}
			if ('0' <= cur && cur <= '9') {
				STATE = NUM_STATE;
			}
			else STATE = OPERATOR_STATE;

			switch (STATE) {
			case NUM_STATE: {
				num = 0;
				while (true) {  //读取个位数以上的数字
					num = 10 * num + (cur - '0');
					if (++i < len) cur = s[i];
					else break;
					if ('0' <= cur && cur <= '9') continue;
					else { i--; break; }  //如果遇到非数字的char,需要字符串需要向前退一格
				}
				num_stack.push(num); //将读取到的数字压入栈中
				if (CAL_flag == 1) {
					compute(num_stack, operator_stack);
				}
				break;
			}
			case OPERATOR_STATE: {
				if (cur == '+' || cur == '-') {
					operator_stack.push(cur);
					CAL_flag = 1;
				}
				else if (cur == '(') CAL_flag = 0;
				else {  //')'的情况
					CAL_flag = 1;
					if (!operator_stack.empty()) {  //如果操作符栈里还有符号,则是'('前被跳过的那个操作符,执行该运算。
						compute(num_stack, operator_stack);
					}
				}
				break;
			}
			}
			i++;
		}
		return num_stack.top();
	}
private:
	std::stack<int> num_stack;
	std::stack<char> operator_stack;
};

思路二 负号看作正号,括号特殊处理

在这里插入图片描述
这个解其实是最简洁最好理解的一个解,附上官方的java代码:以后二刷用C++再写一遍。
它的精髓之处:

  1. 将加减看作同一种运算,用sign的正负1来区分加减;
  2. 用一个result+=来完成正序的加运算(由于1,不用考虑减运算);
  3. *=来左括号'('前的运算。
class Solution {
    public int calculate(String s) {

        Stack<Integer> stack = new Stack<Integer>();
        int operand = 0;
        int result = 0; // For the on-going result
        int sign = 1;  // 1 means positive, -1 means negative

        for (int i = 0; i < s.length(); i++) {

            char ch = s.charAt(i);
            if (Character.isDigit(ch)) {

                // Forming operand, since it could be more than one digit
                operand = 10 * operand + (int) (ch - '0');

            } else if (ch == '+') {

                // Evaluate the expression to the left,
                // with result, sign, operand
                result += sign * operand;

                // Save the recently encountered '+' sign
                sign = 1;

                // Reset operand
                operand = 0;

            } else if (ch == '-') {

                result += sign * operand;
                sign = -1;
                operand = 0;

            } else if (ch == '(') {

                // Push the result and sign on to the stack, for later
                // We push the result first, then sign
                stack.push(result);
                stack.push(sign);

                // Reset operand and result, as if new evaluation begins for the new sub-expression
                sign = 1;
                result = 0;

            } else if (ch == ')') {

                // Evaluate the expression to the left
                // with result, sign and operand
                result += sign * operand;

                // ')' marks end of expression within a set of parenthesis
                // Its result is multiplied with sign on top of stack
                // as stack.pop() is the sign before the parenthesis
                result *= stack.pop();

                // Then add to the next operand on the top.
                // as stack.pop() is the result calculated before this parenthesis
                // (operand on stack) + (sign on stack * (result from parenthesis))
                result += stack.pop();

                // Reset the operand
                operand = 0;
            }
        }
        return result + (sign * operand);
    }
}

思路三 栈和反转字符串

来源:官方解答。这道题十分适合用栈来进行。不过这个方法不是很好想,先放上官方的解答mark一下。
在这里插入图片描述
在这里插入图片描述

class Solution {

    public int evaluateExpr(Stack<Object> stack) {

        int res = 0;

        if (!stack.empty()) {
            res = (int) stack.pop();
        }

        // Evaluate the expression till we get corresponding ')'
        while (!stack.empty() && !((char) stack.peek() == ')')) {

            char sign = (char) stack.pop();

            if (sign == '+') {
                res += (int) stack.pop();
            } else {
                res -= (int) stack.pop();
            }
        }
        return res;
    }

    public int calculate(String s) {

        int operand = 0;
        int n = 0;
        Stack<Object> stack = new Stack<Object>();

        for (int i = s.length() - 1; i >= 0; i--) {

            char ch = s.charAt(i);

            if (Character.isDigit(ch)) {

                // Forming the operand - in reverse order.
                operand = (int) Math.pow(10, n) * (int) (ch - '0') + operand;
                n += 1;

            } else if (ch != ' ') {
                if (n != 0) {

                    // Save the operand on the stack
                    // As we encounter some non-digit.
                    stack.push(operand);
                    n = 0;
                    operand = 0;

                }
                if (ch == '(') {

                    int res = evaluateExpr(stack);
                    stack.pop();

                    // Append the evaluated result to the stack.
                    // This result could be of a sub-expression within the parenthesis.
                    stack.push(res);

                } else {
                    // For other non-digits just push onto the stack.
                    stack.push(ch);
                }
            }
        }

        //Push the last operand to stack, if any.
        if (n != 0) {
            stack.push(operand);
        }

        // Evaluate any left overs in the stack.
        return evaluateExpr(stack);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值