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)if
和else if
中,判断i++
漏了或者错了,导致死循环或者段错误。
(3)单个字符如何转化为字符串?字符串没有这样的构造函数。