leetcode 224 基本计算器

1.题目

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

字符串表达式可以包含左括号 ( ,右括号 ),加号 + ,减号 -,非负整数和空格 。

示例 1:

输入: "1 + 1"
输出: 2

示例 2:

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

示例 3:

输入: "(1+(4+5+2)-3)+(6+8)"
输出: 23

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

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

2.我的题解

双栈法,从前向后读字符串,作如下处理:
(1)空格:跳过;
(2)左括号、加号、减号:入符号栈;
(3)数字:向后找到完整的数字,入数字栈;如果数字栈至少有两个数字且符号栈栈顶是“+”、“-”,进行一次运算,弹出数字栈两个元素、符号栈一个元素,将运算结果入数字栈;
(4)右括号:此时符号栈顶一定是左括号,弹出一次;循环判断是否可以进行运算(即数字栈至少有两个数字且符号栈栈顶是“+”、“-”),进行相关操作直到不能进行为止;

最后符号栈将为空,数字栈只有一个元素,即是结果。

class Solution {
	stack<int> s1;//number
	stack<string> s2;//operator
public:
	int calculate(string s) {
		int len = s.size();
		int i = 0;
		while (i<len) {
			if (s[i] == ' '){//空格
				i++;
				continue;
			}
			else if (s[i] == '(') {//左括号
				s2.push("(");
			}
			else if (s[i] == ')') {//右括号
				s2.pop();//"("
				//calc
				while (!s2.empty() && s1.size() >= 2 && (s2.top() == "+" || s2.top() == "-")) {
					int num2 = s1.top();
					s1.pop();
					int num1 = s1.top();
					s1.pop();
					if (s2.top() == "+")s1.push(num1 + num2);
					else s1.push(num1 - num2);
					s2.pop();
				}
			}
			else if (s[i] == '+') {
				s2.push("+");
			}
			else if (s[i] == '-') {
				s2.push("-");
			}
			else {//number
				int j = i + 1;
				while (s[j] >= '0' && s[j] <= '9')j++;
				s1.push(stoi(s.substr(i,j-i)));
				i = j;

				//calc
				if (s1.size() >= 2 && (s2.top() == "+" || s2.top() == "-")) {
					int num2 = s1.top();
					s1.pop();
					int num1 = s1.top();
					s1.pop();
					if (s2.top() == "+")s1.push(num1 + num2);
					else s1.push(num1 - num2);
					s2.pop();
				}
				continue;
			}
			i++;
		}

		return s1.top();
	}
};

3.别人的题解

3.1 单栈法

仅使用一个栈记录数值,将+、-与数字结合起来,加号为整数,减号为负数,从前向后计算。
优点:适用于本题,实现简单易懂。

class Solution {
	stack<int> s1;//number
	stack<string> s2;//operator
public:
	int calculate(string s) {
		int len = s.size(), res = 0, sign = 1;
		for (int i = 0; i<len; i++) {
			//if (s[i] == ' ')continue;
			if (s[i] >= '0'&&s[i] <= '9') {
				int num = 0;
				while (i<len && s[i] >= '0' && s[i] <= '9') {
					num = 10 * num + s[i] - '0';
					i++;
				}
				res += num*sign;
				i--;
			}
			else if (s[i] == '+')
				sign = 1;
			else if (s[i] == '-')
				sign = -1;
			else if (s[i] == '(') {
				s1.push(res);
				s1.push(sign);
				res = 0;
				sign = 1;
			}
			else if (s[i] == ')') {
				res *= s1.top(); s1.pop();
				res += s1.top(); s1.pop();
			}
		}
		return res;
	}
};

3.2 中缀表达式转后缀表达式

中缀表达式是人类常见的表达式,如1+2*(3+4),而后缀表达式是计算机中常见的表达式,对应的表达式为1 2 3 4 + * +,后缀表达式又称逆波兰表达式,在计算机中可以顺序求解。可以利用栈进行转化,转化规则如下:

顺序查看中缀表达式字符串的每一个字符:
(1)遇到数字时,向后提取出整个数字并输出;
(2)遇到+、-、*、/、(时,如果栈为空,直接入栈;如果栈不为空,弹出并输出栈顶元素直到栈顶元素(操作符)的优先级比当前元素(操作符)优先级低或者栈顶元素为(
(3)遇到)时,弹出并输出栈顶元素直到遇到(,注意(要出栈但是不输出;
(4)查看完字符串后,弹出栈顶元素直到栈为空;

优点:逆波兰表达式的计算是比较容易的,将字符串表达式(中缀)转化为逆波兰表达式再计算表达式的值,这中方法适应性更广,推广也容易,不仅仅适用于本题。

一个判断元素优先级的方法:

	char order[7][7] = {
		//     +   -   *   /   (   )   #
		/*+*/ '>','>','<','<','<','>','>',
		/*-*/ '>','>','<','<','<','>','>',
		/***/ '>','>','>','>','<','>','>',
		/*/*/ '>','>','>','>','<','>','>',
		/*(*/ '<','<','<','<','<','=',' ',
		/*)*/ ' ',' ',' ',' ',' ',' ',' ',
		/*#*/ '<','<','<','<','<',' ','='
	};

下面是中缀转后缀的方法,代码实现如下:

class Solution {
	stack<int> s1;//number
	stack<string> s2;//operator
	char order[7][7] = {
		//     +   -   *   /   (   )   #
		/*+*/ '>','>','<','<','<','>','>',
		/*-*/ '>','>','<','<','<','>','>',
		/***/ '>','>','>','>','<','>','>',
		/*/*/ '>','>','>','>','<','>','>',
		/*(*/ '<','<','<','<','<','=',' ',
		/*)*/ ' ',' ',' ',' ',' ',' ',' ',
		/*#*/ '<','<','<','<','<',' ','='
	};
public:
	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;
	}
};

转化为后缀表达式之后,可以使用leetcode 150的办法计算。

4.总结与反思

(1)没有自动补全、代码检查确实不习惯,很多地方top函数忘了加括号、左右括号没有匹配好。
(2)ifelse if中,判断i++漏了或者错了,导致死循环或者段错误。
(3)单个字符如何转化为字符串?字符串没有这样的构造函数。

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

猜你喜欢

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