目录
1) (后缀计算)leetcode 150. 逆波兰表达式求值
3) leetcode 224. 基本计算器 && 227. 基本计算器 II && 772. 基本计算器 III
0) 总结记忆
1. 后缀计算: 一个栈,遍历字符串,遇到数字,压栈,遇到运算符,栈弹两个计算再压栈。
2. 中缀转后缀: 两个栈,栈1保存中间结果,栈2保存运算符(优先级从低到高),遍历字符串,遇到数字,压栈1,遇到运算符,压栈2,且可能有弹栈操作,弹出的运算符到栈1去。最后栈1逆序输出。
1)定义
中缀表达式就是我们日常写的表达式,如 a+(b-c)*d
,
而后缀表达式则是运算符在操作数后面称为后缀表达式(也称逆波兰表达式),编译系统将中缀表达式改写 abc-d*+ 方便运算。
tips: 中缀转后缀,得到的后缀表达式不唯一,要想验证两个后缀表示式是一致的,把它转回中缀表达式,进行对比。
2)后缀表达式求值
其求值过程可以用到栈来辅助存储。
基本过程:
- 用一个栈,遍历表达式,遇到数字则压入栈中,遇到运算符则从栈中弹出两个数,并将计算后的结果压回栈中。
比如待求值的后缀表达式为:6 5 2 3 + 8 * + 3 + *,则其求值过程如下:
1)遍历表达式,遇到数字则压入栈中
2)接着读到“+”,则弹出3和2,执行3+2,计算结果等于5,并将5压入到栈中。
3)读到8,将其直接放入栈中。
4)读到“*”,弹出8和5,执行8*5,并将结果40压入栈中。而后过程类似,读到“+”,将40和5弹出,将40+5的结果45压入栈...以此类推。最后求的值288。
3)中缀转后缀
- 初始化两个栈:运算符栈s1和储存中间结果的栈s2;
- 从左至右扫描中缀表达式;
- 遇到操作数时,将其压s2;
- 遇到运算符时,比较其与s1栈顶运算符的优先级:
如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
否则,若优先级比栈顶运算符的高,也将运算符压入s1
否则,将s1栈顶的运算符弹出并压入到s2中,再次转到(4-1)与s1中新的栈顶运算符相比较。(栈顶与当前优先级相同也弹)
总结就是: 弹出优先级大于等于自己的运算符号。(左括号的优先级最低为0)
5. 遇到括号时:
如果是左括号“(”,则直接压入s1;
如果是右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃;
6. 重复步骤2至5,直到表达式的最右边;
7. 将s1中剩余的运算符依次弹出并压入s2;
8. 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式
然而实际代码并不用两个栈,用一个stack 保存 运算符即可,并且考虑到可能一个运算数是两位或两位以上,所以应该用vector<string> 保存后缀结果。
4) 代码
1) (后缀计算)leetcode 150. 逆波兰表达式求值
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> sta;
for(auto& i:tokens){
if(i=="+" || i== "-" || i=="*" || i=="/"){
int res;
int right=sta.top();
sta.pop();
int left=sta.top();
sta.pop();
if(i=="+")
res=left+right;
else if(i=="-")
res= left-right;
else if(i=="*")
res =left*right;
else
res= left/right;
sta.push(res);
} else
sta.push(stoi(i));
}
return sta.top();
}
};
2)中缀转后缀
// 转换str格式 使其满足要求
string strFormat(string str){
string s;
for(auto i:str){ // 删除空格
if(i==' ') continue;
s += i;
}
string res;
for(int i=0;i<s.size();++i){ //解决(-1+2)负数报错问题
if(s[i]=='-') //即在 -1 前面加一个0 变成 0-1 即可
if(i==0 || s[i-1]=='(')
res += '0';
res += s[i];
}
return res;
}
//中缀转后缀
vector<string> toPost(string s){
stack<char> sta;
string one; // 可能是两位数
vector<string> post;
for(char& i:s){
if(i>='0' && i<='9')
one += i;
else{
if(one.size()){
post.push_back(one);
one="";
}
if(i=='(') sta.push(i);
else if(i==')'){
while(sta.top()!='('){
post.push_back(string(1,sta.top()));
sta.pop();
}
sta.pop();
}
else if(i=='+' || i=='-'){
while(sta.size() && sta.top()!='('){
post.push_back(string(1,sta.top()));
sta.pop();
}
sta.push(i);
}
else if(i=='*' || i=='/'){
while(sta.size() && (sta.top()=='*' || sta.top() == '/') ){
post.push_back(string(1,sta.top()));
sta.pop();
}
sta.push(i);
}
}
}
if(one.size()) post.push_back(one); // 别忘了检查one 可能还有值没存
while(sta.size()){
post.push_back(string(1,sta.top()));
sta.pop();
}
return post;
}
3) leetcode 224. 基本计算器 && 227. 基本计算器 II && 772. 基本计算器 III
记这个!, 直接中缀表达式求值
中缀表达式求值是通过两个栈来实现的。其中一个保存操作数的栈,另一个是保存运算符的栈。
我们从左向右遍历表达式,当遇到数字,我们就直接压入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较。
如果比运算符栈顶元素的优先级高,就将当前运算符压入栈;
如果比运算符栈顶元素的优先级低或者相同,从运算符栈中取栈顶运算符,从操作数栈的栈顶取 2 个操作数,然后进行计算,再把计算完的结果压入操作数栈,继续比较。
‘(’ 的优先级最低为0
class Solution {
public:
int calculate(string s) {
str_format(s); // 格式化s
stack<int> nums; // 操作数
stack<char> ops; // 运算符号
/*
中缀表达式求值是通过两个栈来实现的。其中一个保存操作数的栈,另一个是保存运算符的栈。
我们从左向右遍历表达式,当遇到数字,我们就直接压入操作数栈;当遇到运算符,就与运算符栈的栈顶元素进行比较。
如果比运算符栈顶元素的优先级高,就将当前运算符压入栈;
如果比运算符栈顶元素的优先级低或者相同,从运算符栈中取栈顶运算符,从操作数栈的栈顶取 2 个操作数,然后进行计算,再把计算完的结果压入操作数栈,继续比较。
‘(’ 的优先级最低为0
*/
unordered_map<char, function<int(int, int)>> op_2_calc = {
{'+', plus<int>()},
{'-', minus<int>()},
{'*', [](int num1, int num2){return num1 * num2;}},
{'/', [](int num1, int num2) {return num1 / num2;}}
};
unordered_map<char, int> op_2_priority = {
{'(', 0},
{'+', 1},
{'-', 1},
{'*', 2},
{'/', 2},
};
for (int i = 0; i < s.size(); i++)
{
char c = s[i];
if (c <= '9' && c >= '0')
{
int one = 0;
while (i < s.size() && isdigit(s[i]))
{
one = one * 10 + (s[i] - '0');
++i;
}
--i;
nums.push(one);
} else if (op_2_calc.count(c)) {
while (ops.size() && op_2_priority[c] <= op_2_priority[ops.top()]){
int num2 = nums.top();
nums.pop();
int num1 = nums.top();
nums.pop();
nums.push(op_2_calc[ops.top()](num1, num2));
ops.pop();
}
ops.push(c);
} else if (c == '(') {
ops.push(c);
} else if (c == ')') {
while (ops.size() && ops.top() != '(')
{
int num2 = nums.top();
nums.pop();
int num1 = nums.top();
nums.pop();
nums.push(op_2_calc[ops.top()](num1, num2));
ops.pop();
}
ops.pop();
}
}
return nums.top();
}
void str_format(string& s)
{
string t = "("; // 首尾加 () 保证会被计算
/*
1. 删除空格
2. 对于 -1 + 2 或者 +1 + 2 这种+ -号不是表示运算符 而是表示正负号的,变为 0-1+2
*/
for (char c: s){
if (c == ' ')
continue;
else if ((c == '+' || c == '-') && (t.back() == '(')) {
t += '0';
t += c;
} else {
t += c;
}
}
t += ')';
s = t;
return;
}
};
class Solution {
public:
using LL=long long; // 用int 772题 会溢出
LL calculate(string s) {
string str=strFormat(s);
vector<string> vec=toPost(str);
LL res = evalRPN(vec);
return res;
}
// 转换str格式 使其满足要求
string strFormat(string str){
string s;
for(auto i:str){ // 删除空格
if(i==' ') continue;
s += i;
}
string res;
for(LL i=0;i<s.size();++i){ //解决(-1+2)负数报错问题
if(s[i]=='-') //即在 -1 前面加一个0 变成 0-1 即可
if(i==0 || s[i-1]=='(')
res += '0';
res += s[i];
}
return res;
}
//中缀转后缀
vector<string> toPost(string s){
stack<char> sta;
string one; // 可能是两位数
vector<string> post;
for(char& i:s){
if(i>='0' && i<='9')
one += i;
else{
if(one.size()){
post.push_back(one); // one 有值就加进去
one="";
}
if(i=='(') sta.push(i);
else if(i==')'){
while(sta.top()!='('){
post.push_back(string(1,sta.top())); // 不能直接用sta.top 因为 post放的是string,而sta存的是char
sta.pop();
}
sta.pop();
}
else if(i=='+' || i=='-'){
while(sta.size() && sta.top()!='('){
post.push_back(string(1,sta.top()));
sta.pop();
}
sta.push(i);
}
else if(i=='*' || i=='/'){
while(sta.size() && (sta.top()=='*' || sta.top() == '/') ){
post.push_back(string(1,sta.top()));
sta.pop();
}
sta.push(i);
}
}
}
if(one.size()) post.push_back(one); // 别忘了检查one 可能还有值没存
while(sta.size()){
post.push_back(string(1,sta.top()));
sta.pop();
}
return post;
}
// 后缀计算
LL evalRPN(vector<string>& tokens) {
stack<long long > sta;
for(auto& i:tokens){
if(i=="+" || i== "-" || i=="*" || i=="/"){
LL res;
LL right=sta.top();
sta.pop();
LL left=sta.top();
sta.pop();
if(i=="+")
res=left+right;
else if(i=="-")
res= left-right;
else if(i=="*")
res =left*right;
else
res= left/right;
sta.push(res);
} else
sta.push(stoll(i));
}
return sta.top();
}
};