Leetcode——栈和队列(3)
基本计算器
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式可以包含左括号 (
,右括号 )
,加号 +
,减号 -
,非负整数和空格
。
示例 1:
输入: "1 + 1"
输出: 2
示例 2:
输入: " 2-1 + 2 "
输出: 3
示例 3:
输入: "(1+(4+5+2)-3)+(6+8)"
输出: 23
说明:
- 你可以假设所给定的表达式都是有效的。
- 请不要使用内置的库函数
eval
。
栈
需要一个栈来辅助计算,用个变量sign来表示当前的符号,我们遍历给定的字符串s,
如果遇到了数字,由于可能是个多位数,所以我们要用while循环把之后的数字都读进来,然后用sign*num
来更新结果res
;
如果遇到了加号,则sign
赋为1,
如果遇到了符号,则赋为-1;
如果遇到了左括号,则把当前结果res和符号sign压入栈,res重置为0,sign重置为1;
如果遇到了右括号,结果res乘以栈顶的符号,栈顶元素出栈,结果res加上栈顶的数字,栈顶元素出栈。
class Solution {
public:
int calculate(string s) {
int res = 0, sign = 1, n = s.size();
stack<int> st;
for (int i = 0; i < n; ++i) {
char c = s[i];
if (c >= '0') {
int num = 0;
while (i < n && s[i] >= '0') {
num = 10 * num + (s[i++] - '0');
}
res += sign * num;
--i;
} else if (c == '+') {
sign = 1;
} else if (c == '-') {
sign = -1;
} else if (c == '(') {
st.push(res);
st.push(sign);
res = 0;
sign = 1;
} else if (c == ')') {
res *= st.top(); st.pop();
res += st.top(); st.pop();
}
}
return res;
}
};
递归
我们用一个变量cnt
,遇到左括号自增1,遇到右括号自减1,
当cnt
为0的时候,说明括号正好完全匹配,这个trick在验证括号是否valid
的时候经常使用到。
然后我们就是根据左右括号的位置提取出中间的子字符串调用递归函数,返回值赋给num
class Solution {
public:
int calculate(string s) {
int res = 0, num = 0, sign = 1, n = s.size();
for (int i = 0; i < n; ++i) {
char c = s[i];
if (c >= '0' && c <= '9') {
num = 10 * num + (c - '0');
} else if (c == '(') {
int j = i, cnt = 0;
for (; i < n; ++i) {
if (s[i] == '(') ++cnt;
if (s[i] == ')') --cnt;
if (cnt == 0) break;
}
num = calculate(s.substr(j + 1, i - j - 1));
}
if (c == '+' || c == '-' || i == n - 1) {
res += sign * num;
num = 0;
sign = (c == '+') ? 1 : -1;
}
}
return res;
}
};
基本计算器 II
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+
, -
,*
,/
四种运算符和空格
。 整数除法仅保留整数部分。
示例 1:
输入: "3+2*2"
输出: 7
示例 2:
输入: " 3/2 "
输出: 1
示例 3:
输入: " 3+5 / 2 "
输出: 5
说明:
- 你可以假设所给定的表达式都是有效的。
- 请不要使用内置的库函数
eval
。
栈
由于存在运算优先级,我们采取的措施是使用一个栈保存数字,
如果该数字之前的符号是加或减,那么把当前数字压入栈中,注意如果是减号,则加入当前数字的相反数,因为减法相当于加上一个相反数。
如果之前的符号是乘或除,那么从栈顶取出一个数字和当前数字进行乘或除的运算,再把结果压入栈中,那么完成一遍遍历后,所有的乘或除都运算完了,再把栈中所有的数字都加起来就是最终结果了
class Solution {
public:
int calculate(string s) {
long res = 0, num = 0, n = s.size();
char op = '+';
stack<int> st;
for (int i = 0; i < n; ++i) {
if (s[i] >= '0') {
num = num * 10 + s[i] - '0';
}
if ((s[i] < '0' && s[i] != ' ') || i == n - 1) {
if (op == '+') st.push(num);
if (op == '-') st.push(-num);
if (op == '*' || op == '/') {
int tmp = (op == '*') ? st.top() * num : st.top() / num;
st.pop();
st.push(tmp);
}
op = s[i];
num = 0;
}
}
while (!st.empty()) {
res += st.top();
st.pop();
}
return res;
}
};
方法二
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;
}
};
基本计算器 III
1.题目描述
实现一个基本的计算器来计算简单的表达式字符串。
表达式字符串可以包含左括号 ( 和右括号 ),加号 + 和减号 -,非负 整数和空格 。
表达式字符串只包含非负整数, +, -, *, / 操作符,左括号 ( ,右括号 )和空格 。整数除法需要向下截断。
你可以假定给定的字符串总是有效的。所有的中间结果的范围为 [-2147483648, 2147483647]。
一些例子:
"1 + 1" = 2
" 6-4 / 2 " = 4
"2*(5+5*2)/3+(6/2+8)" = 21
"(2+6* 3+5- (3*14/7+2)*5)+3"=-12
注:不要 使用内置库函数 eval。
num
表示当前的数字,curRes 表示当前的结果,res
为最终的结果,op
为操作符号,初始化为 '+'。
当遇到数字的时候,我们将 num 自乘以 10 并加上这个数字,这是由于可能遇到多位数,所以每次要乘以 10。
当遇到括号的时候,这里就有一个小 trick,由于表示可能会有括号嵌套括号,所以我们如果搜索右括号的话,就有可能使得括号没有正确的匹配上,所以我们用一个变量 cnt,遇到左括号自增1,遇到右括号自减1,当 cnt 为0的时候,说明括号正好完全匹配,这个 trick 在验证括号是否 valid 的时候经常使用到。
然后我们就是根据左右括号的位置提取出中间的子字符串调用递归函数,返回值赋给 num
。
如果遇到符号,或者是最后一个位置的字符时,我们根据 op 的值对 num
进行分别的加减乘除的处理,结果保存到 curRes
中。然后再次判读如果 op 是加或减,或者是最后一个位置的字符时,将 curRes
加到结果 res 中,并且 curRes
重置为0。
最后将当前字符c赋值给 op(注意这里只有当时最后一个位置的字符时,才有可能不是运算符号,不过也不要紧了,因为遍历已经结束了),num
也要重置为0
class Solution {
public:
int calculate(string s) {
int n = s.size(), num = 0, curRes = 0, res = 0;
char op = '+';
for (int i = 0; i < n; ++i) {
char c = s[i];
if (c >= '0' && c <= '9') {
num = num * 10 + c - '0';
} else if (c == '(') {
int j = i, cnt = 0;
for (; i < n; ++i) {
if (s[i] == '(') ++cnt;
if (s[i] == ')') --cnt;
if (cnt == 0) break;
}
num = calculate(s.substr(j + 1, i - j - 1));
}
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;
}
};
基本计算器 IV
给定一个表达式 expression
如 expression = "e + 8 - a + 5"
和一个求值映射,如 {"e": 1}
(给定的形式为 evalvars = ["e"]
和 evalints = [1]
),返回表示简化表达式的标记列表,例如 ["-1*a","14"]
- 表达式交替使用块和符号,每个块和符号之间有一个空格。
- 块要么是括号中的表达式,要么是变量,要么是非负整数。
- 块是括号中的表达式,变量或非负整数。
- 变量是一个由小写字母组成的字符串(不包括数字)。请注意,变量可以是多个字母,并注意变量从不具有像
"2x"
或"-x"
这样的前导系数或一元运算符 。
表达式按通常顺序进行求值:先是括号,然后求乘法,再计算加法和减法。例如,expression = "1 + 2 * 3"
的答案是 ["7"]
。
输出格式如下:
- 对于系数非零的每个自变量项,我们按字典排序的顺序将自变量写在一个项中。例如,我们永远不会写像
“b*a*c”
这样的项,只写“a*b*c”
。 - 项的次数等于被乘的自变量的数目,并计算重复项。(例如,
"a*a*b*c"
的次数为 4。)。我们先写出答案的最大次数项,用字典顺序打破关系,此时忽略词的前导系数。 - 项的前导系数直接放在左边,用星号将它与变量分隔开(如果存在的话)。前导系数 1 仍然要打印出来。
- 格式良好的一个示例答案是
["-2*a*a*a", "3*a*a*b", "3*b*b", "4*a", "5*c", "-6"]
。 - 系数为 0 的项(包括常数项)不包括在内。例如,“0” 的表达式输出为 []。
示例:
输入:expression = "e + 8 - a + 5", evalvars = ["e"], evalints = [1]
输出:["-1*a","14"]
输入:expression = "e - 8 + temperature - pressure",
evalvars = ["e", "temperature"], evalints = [1, 12]
输出:["-1*pressure","5"]
输入:expression = "(e + 8) * (e - 8)", evalvars = [], evalints = []
输出:["1*e*e","-64"]
输入:expression = "7 - 7", evalvars = [], evalints = []
输出:[]
输入:expression = "a * b * c + b * a * c * 4", evalvars = [], evalints = []
输出:["5*a*b*c"]
输入:expression = "((a - b) * (b - c) + (c - a)) * ((a - b) + (b - c) * (c - a))",
evalvars = [], evalints = []
输出:["-1*a*a*b*b","2*a*a*b*c","-1*a*a*c*c","1*a*b*b*b","-1*a*b*b*c","-1*a*b*c*c","1*a*c*c*c","-1*b*b*b*c","2*b*b*c*c","-1*b*c*c*c","2*a*a*b","-2*a*a*c","-2*a*b*b","2*a*c*c","1*b*b*b","-1*b*b*c","1*b*c*c","-1*c*c*c","-1*a*a","1*a*b","1*a*c","-1*b*c"]
提示:
expression
的长度在[1, 250]
范围内。evalvars, evalints
在范围[0, 100]
内,且长度相同。
typedef int Coefficient;
typedef multiset<string> Polynomial;
struct Cmp
{
bool operator()(const Polynomial &a, const Polynomial&b)const
{
if (a.size() < b.size())//次数高的放前面
return false;
else if (a.size() > b.size())
return true;
else
return a < b;//次数相同,字典序小的放前面
}
};
typedef map<Polynomial, Coefficient,Cmp> Polynomials;
typedef pair<string, int> E_Value_Pair;
class Solution {
public:
vector<string> basicCalculatorIV(string expression, vector<string> &evalvars, vector<int> &evalints) {
stack<Polynomials>opd;//放操作数
stack<char>opr;//放运算符
char c;
char pre_opr;
char pre_c='#';
expression += "#";
unordered_map<string,int>e_value_map;
for (int i = 0; i < evalvars.size(); i++)
{
e_value_map.insert(pair<string,int>(evalvars[i], evalints[i]));
}
//opd.push(Polynomials());
opr.push('#');
int max_len = expression.size();
for (int i = 0; i < max_len; i++)
{
c = expression[i];
pre_opr = opr.top();
if (c == '+'||c=='-'||c==')'||c=='#')//要考虑作为数字的一元运算符
{
while(pre_opr == '+' || pre_opr == '-'|| pre_opr == '*' || pre_opr == '/')
{
opr.pop();
if(pre_opr=='+')
{
auto top = opd.top();
if (pre_c != '#' && pre_c != '(')//只有在作为二元运算符的时候才考虑
{
opd.pop();
add(opd.top(), top);
}
}
else if (pre_opr == '-')
{
auto top = opd.top();
if (pre_c != '#' && pre_c != '(')//2元运算
{
opd.pop();
sub(opd.top(), top);
}
else
{
multi(opd.top(), Polynomials{ {Polynomial{""},-1} });//相当于乘上-1
}
}
else if (pre_opr == '*')
{
auto top = opd.top();
opd.pop();
multi(opd.top(), top);
}
else if (pre_opr == '/')
{
auto top = opd.top();
opd.pop();
div(opd.top(), top);
}
pre_opr = opr.top();
}
if (pre_opr == '('&&c==')')
opr.pop();
else
opr.push(c);
}
else if (c == '*' || c == '/' )
{
while ( pre_opr == '*' || pre_opr == '/')
{
if (pre_opr == '*')
{
auto top = opd.top();
opd.pop();
multi(opd.top(), top);
}
else if (pre_opr == '/')
{
auto top = opd.top();
opd.pop();
div(opd.top(), top);
}
opr.pop();
pre_opr = opr.top();
}
opr.push(c);
}
else if (c >= '0'&&c <= '9')//常数项作为空串
{
int start = i;
int len = 0;
for (; i < max_len&&expression[i] >= '0'&&expression[i] <= '9'; i++, len++);
string str = expression.substr(start, len);
int num;
sscanf(str.c_str(), "%d", &num);
//if(num!=0)
opd.push(Polynomials{ {Polynomial(),num} });//常数项不应该设为0
i--;
}
else if (c >= 'a'&&c <= 'z')//处理单个表达式
{
int start = i;
int len = 0;
for (; i < max_len&&expression[i] >= 'a'&&expression[i] <= 'z'; i++, len++);
string name = expression.substr(start, len);
unordered_map<string, int>Num;
auto iter = e_value_map.find(name);
if (iter == e_value_map.end())//非常数项
{
opd.push(Polynomials{ {Polynomial{name},1} });
}
else//在 当作常数项
{
//if(iter->second!=0)
opd.push(Polynomials{ {Polynomial(),iter->second} });
}
i--;
}
else if (c == '(')
{
opr.push('(');
//opd.push(Polynomials());
}
if (c!=' ')
pre_c = c;
}
//print(opd.top());
if (opd.empty())
return vector<string>();
else
return get_result(opd.top());
}
void add(Polynomials&a, const Polynomials&b)//把b都放到a里面
{
for (auto p : b)
{
auto iter = a.find(p.first);
if (iter == a.end())
{
a.insert(p);
}
else if(iter->second+p.second==0)
{
a.erase(iter);
}
else
{
iter->second += p.second;
}
}
}
void sub(Polynomials&a, const Polynomials&b)
{
for (auto p : b)
{
auto iter = a.find(p.first);
if (iter == a.end())
{
p.second = -p.second;
a.insert(p);
}
else if (iter->second - p.second == 0)
{
a.erase(iter);
}
else
{
iter->second -= p.second;
}
}
}
void multi(Polynomials&a, const Polynomials&b)
{
Polynomials ret;
for (auto pb : b)
{
if (pb.second == 0)
continue;
for (auto pa : a)
{
if (pa.second == 0)
continue;
Coefficient co = pa.second*pb.second;
Polynomial tmp = pa.first;
//merge 函数时c++17的,所以得自己实现
for (auto word : pb.first)
{
tmp.insert(word);
}
add(ret, Polynomials{ {tmp,co} });
}
}
a=ret;//貌似也不是很复杂嘛?搞错了
}
void div(Polynomials&a, const Polynomials&b)
{
Polynomials ret;
for (auto p : b)
{
Polynomials tmp{ p };
add(ret, tmp);
}
a = ret;//貌似也不是很复杂嘛?
}
void print(Polynomials&ps)
{
for (auto p : ps)
{
cout << p.second;
for (auto little_p : p.first)
{
cout << "*" << little_p.c_str();
}
cout << endl;
}
}
vector<string> get_result(Polynomials&ps)
{
//这里仔细看一下对0的处理,你可能纳闷了,加减乘不是处理了0的问题了吗,
//但是还有一个问题,你直接读入一个0或者某个word的值是0,
//你就会插入到栈里面,我试过不插入,但好麻烦,所以我直接在最后处理好了。
vector<string> ret;
for (auto p : ps)
{
string line;
//cout << p.second;
if(p.second!=0)
line +=to_string(p.second);
for (auto little_p : p.first)
{
//cout << "*" << little_p.c_str();
line += "*" + little_p;
}
if(!line.empty())
ret.push_back(line);
//cout << endl;
}
return ret;
}
};