leetcode 227 基本计算器II

1.题目

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

字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。

示例 1:

输入: "3+2*2"
输出: 7

示例 2:

输入: " 3/2 "
输出: 1

示例 3:

输入: " 3+5 / 2 "
输出: 5

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/basic-calculator-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.我的题解

2.1逆波兰表达式

参见leetcode 224的解法,首先将中缀表达式转化为后缀表达式,再计算后缀表达式的值。当然这样的时间耗费和空间耗费都是挺大的。优点是通用。

class Solution {
    stack<int> s1;
    stack<string> s2;
    char order[7][7] = {
		//     +   -   *   /   (   )   #
		/*+*/ '>','>','<','<','<','>','>',
		/*-*/ '>','>','<','<','<','>','>',
		/***/ '>','>','>','>','<','>','>',
		/*/*/ '>','>','>','>','<','>','>',
		/*(*/ '<','<','<','<','<','=',' ',
		/*)*/ ' ',' ',' ',' ',' ',' ',' ',
		/*#*/ '<','<','<','<','<',' ','='
	};
public:
    int calculate(string s) {
        vector<string> res=mid2post(s);
        return evalRPN(res);
    }
    int evalRPN(vector<string>& tokens) {
        stack<int>().swap(s1);
        int len=tokens.size();
        if(len==0){
            return 0;
        }
        for(int i=0;i<len;i++){
            if(tokens[i].length()==1 && (tokens[i][0]=='+' ||tokens[i][0]=='-' || 
               tokens[i][0]=='/' || tokens[i][0]=='*')){
                int num2 = s1.top();
				s1.pop();
				int num1 = s1.top();
				s1.pop();
                if(tokens[i][0]=='+')s1.push(num1+num2);
                else if(tokens[i][0]=='-')s1.push(num1-num2);
                else if(tokens[i][0]=='*')s1.push(num1*num2);
                else if(tokens[i][0]=='/')s1.push(num1/num2);
                
            }
            else{
                s1.push(stoi(tokens[i]));
            }
        }

        return s1.top();
    }

    vector<string> mid2post(string s) {
		vector<string> res;
		stack<string>().swap(s2);//clear

		for (int i = 0; i < s.size(); i++) {
			if (s[i] == ' ')continue;
			else if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i]=='(') {
				if (s2.empty()) {//第一次,直接入栈
					s2.push(toString(s[i]));
				}
				else {//栈中运算符高的出栈
					while (!s2.empty() && order[getOrder(s2.top()[0])][getOrder(s[i])] == '>') {
						if(s2.top()!="(")res.push_back(s2.top());//直到遇到(
						else break;
						s2.pop();
					}
					s2.push(toString(s[i]));//当前符号入栈
				}
			}
			else if (s[i] == ')') {
				while (!s2.empty() && s2.top()!="(") {//直接出栈直到遇到(
					res.push_back(s2.top());
					s2.pop();
				}
				s2.pop();//"("
			}
			else {
				int j = i + 1;
				while (s[j] >= '0'&&s[j] <= '9')j++;
				res.push_back(s.substr(i, j - i));
				i=j-1;//for循环i自己会++,所以i=j-1
			}
		}
		while (!s2.empty()) {//最后不要忘了清空栈
			res.push_back(s2.top());
			s2.pop();
		}
		return res;
	}

	int getOrder(char s) {
		if (s == '+')return 0;
		else if (s == '-')return 1;
		else if (s == '*')return 2;
		else if (s == '/')return 3;
		else if (s == '(')return 4;
		else if (s == ')')return 5;
		else if (s == '#')return 6;
		return -1;
	}

	string toString(char ch) {//单个字符转字符串
		string res = "a";
		res[0] = ch;
		return res;
	}
};

2.2 双栈法

class Solution {
    stack<int> s1;
    stack<char> s2;
public:
    int calculate(string s) {
        for(int i=0;i<s.size();i++){
            if(s[i]==' ')continue;
            if(s[i]=='+' || s[i]=='-'){
                while(!s2.empty())calc();
                s2.push(s[i]);
            }
            else if(s[i]=='*' || s[i]=='/'){
                while(!s2.empty() && (s2.top()=='*' || s2.top()=='/'))calc();
                s2.push(s[i]);
            }
            else{
                int j=i+1;
                while(s[j]>='0' && s[j]<='9')j++;
                s1.push(stoi(s.substr(i,j-i)));
                i=j-1;
            }
        }

        while(!s2.empty())calc();
        return s1.top();
    }

    int getValue(int num1,int num2 ,char ch){
        if(ch=='+')return num1+num2;
        else if(ch=='-')return num1-num2;
        else if(ch=='*')return num1*num2;
        else if(ch=='/')return num1/num2;
        return 0;
    }

    void calc(){
        int num2=s1.top();s1.pop();
        int num1=s1.top();s1.pop();
        s1.push(getValue(num1,num2,s2.top()));
        s2.pop();
    }
};

3.别人的题解

上面两种解法的时间耗费和空间耗费都比较高,分析一下开销主要在以下方面:
(1)栈的操作:STL库本来就慢;
(2)string.substr(int start,int len),stoi(string s)
(3)函数的调用;
一个时间和空间相对较优的实现如下:

class Solution {
public:
    int calculate(string s) {
        long res = 0, curRes = 0, num = 0, n = s.size();
        char op = '+';
        for (int i = 0; i < n; ++i) {
            char c = s[i];
            if (c >= '0' && c <= '9') {
                num = num * 10 + c - '0';
            }
            if (c == '+' || c == '-' || c == '*' || c == '/' || i == n - 1) {
                switch (op) {
                    case '+': curRes += num; break;
                    case '-': curRes -= num; break;
                    case '*': curRes *= num; break;
                    case '/': curRes /= num; break;
                }
                if (c == '+' || c == '-' || i == n - 1) {
                    res += curRes;
                    curRes = 0;
                }
                op = c;
                num = 0;
            } 
        }
        return res;
    }
};

4.总结与反思

(1)for循环中尽量避免改变循环变量的值,一般都有办法一个一个读,使用变量记录即可。(如本题找一个完整的数,可以用一个int变量记录)
(2)为了时空间耗费最优,少调用花里胡哨的函数。

发布了32 篇原创文章 · 获赞 0 · 访问量 434

猜你喜欢

转载自blog.csdn.net/weixin_43951240/article/details/103863050