目录
1. 概念
逆波兰表达式,也称为后缀表达式在这种表达式中所有的操作符置于操作数的后面。这种表达式格式特别适合用于计算机程序的算数运算,因为他能避免使用括号来标识操作符的优先级,从而简化计算过程。
2. 中缀表达式转换为后缀表达式
依次读取计算表达式中的值,遇到运算数直接输出(或存储起来)。
建立一个栈来存储运算符,利用栈后进先出的性质,在遇到后面运算符时出栈里面存储的前面的运算符进行比较,确定优先级。
(1) 遇到运算符,如果栈为空或者栈不为空且当前运算符比栈运算符优先级高,则当前运算符入栈。因为如果栈里面存储的是前一个运算符,当前运算符比前一个优先级高,说明前一个不能运算,当前运算符也不能运算,因为后面可能有更高优先级的运算符。
(2) 遇到运算符,如果栈不为空且当前运算符比栈顶运算符优先级低或相等,说明栈顶的运算符可以运算了,输出(存储)栈顶运算符,当前运算符继续走前面遇到运算符的逻辑进行判断
如果遇到() 则把括号的计算表达式当成一个子表达式,进行递归处理子表达式,处理后转换出的后缀表达式加载前面表达式的后面输出(或存储)即可
输出栈中剩余的所有运算符。
isdigit函数
此处为了方便先介绍一个库函数isdigit()
此函数用来检查一个字符是否为数字,是 返回true,不是 返回false
在其他语言中功能或有差异例如python中是检测字符串是否为数字,此处不过多赘述
运算符优先级比较
运算符'+' '-' '*' '/' 的优先级显然不可以直接根据ASCII值进行比较
可通过结构体来设置一个结构体数组,再通过函数进行比较
struct Priority
{
char _op;
int _bp;
};
Priority p[]= { {'+',1},{'-',1},{'*',2},{'/',2} };
或者直接通过map进行更方便的比较()
map<char, int> OperatorPriority = { {'+',1},{'-',1},{'*',2},{'/',2} };
代码实现
其中函数toRPN的第二个参数为了方便递归,第三的是为了方便下面的题目
map<char, int> OperatorPriority = { {'+',1},{'-',1},{'*',2},{'/',2} };
class conversion
{
public:
//中缀表达式转后缀表达式
void toRPN(const string& s, size_t& i, vector<string>& v)
{
stack<char> st;
while (i < s.size())
{
if (isdigit(s[i]))//isdigit:若是数字0到九就返回true
{
string tmp;
while (i < s.size() && isdigit(s[i]))
{
tmp += s[i];
i++;
}
v.push_back(tmp);
}
else
{
if (s[i] == '(')
{
i++;
toRPN(s, i, v);
}
else if (s[i] == ')')
{
while (!st.empty())
{
v.push_back(string(1, st.top()));
st.pop();
}
i++;
return;
}
else if (st.empty() || OperatorPriority[s[i]] > OperatorPriority[st.top()])//在上面两个判断下,不然(或)就会入栈
{
st.push(s[i]);
i++;
}
else
{
v.push_back(string(1, st.top()));
st.pop();
}
}
}
while (!st.empty())
{
v.push_back(string(1, st.top()));
st.pop();
}
}
};
3. 力扣 150 逆波兰表达式求值
题目介绍:
给你一个字符串数组
tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。请你计算该表达式。返回一个表示表达式值的整数。
注意事项:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。- 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
stoi函数
我们先简单介绍一下stoi来方便我们解题
stoi是C++标准库里的一个函数,主要作用是将字符串转换为整数,可以处理不同进制的数字字符串,并将其转换为十进制的整数。
stoi 函数的原型如下:
int stoi(const std::string& str , std::size_t* pos = nullptr , int base = 10);
其中
1. str 是要转换的字符串。
2. pos 是一个指向 size_t 的指针,用于存储转换停止的位置。如果不需要这个信息,可以设置为 nullptr。
3. base 表示字符串中数字的进制,默认是 10 进制。如果设置为 0,进制会根据字符串的格式自动判断。
其余先不作说明。
具体思路
整体很简单,1. 我们先建立一个栈来存储整数
2. 遇到操作符我们就出掉栈里的两个元素
3. 让其进行对应的运算符操作后再进栈
4. 直到数组走完,此时栈顶就是最后的值,返回栈顶元素即可
代码
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(auto e:tokens)
{
if(!(e=="+"||e=="-"||e=="*"||e=="/"))
{
st.push(stoi(e));//stoi//将字符串转化为整数
}
else
{
int sum1=st.top();
st.pop();
int sum2=st.top();
st.pop();
if(e=="+")
{
st.push(sum1+sum2);
}
else if(e=="-")
{
st.push(sum2-sum1);
}
else if(e=="*")
{
st.push(sum2*sum1);
}
else if(e=="/")
{
st.push(sum2/sum1);
}
}
}
return st.top();
}
};
4. 力扣224 基本计算器
题目介绍
给你一个字符串表达式 s
,请你实现一个基本计算器来计算并返回它的值。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval()
。
示例 1:
输入:s = "1 + 1" 输出:2
示例 2:
输入:s = " 2-1 + 2 " 输出:3
示例 3:
输入:s = "(1+(4+5+2)-3)+(6+8)" 输出:23
我们将前两个的代码先粘贴到此题后
逻辑细节
根据示例我们发现,给定的字符串有空格,我们上面的代码无法处理空格,于是我们可以新建一个字符串接收给定字符串的值,遇到' '就不接收,就可以得到一个无空格的表达式
string news;
for(auto e:s)
{
if(e!=' ')
news+=e;
}
此时我们发现
有负号我们就无法处理了,我们可以在中缀转后缀那里更改,但是逻辑有一些麻烦。
所以我们可以将负号'-'转换为"0-"来加入到字符串中,什么时候'-'是负号呢
假如当前s[i] 是字符 '-'
1. '-' 前面的一位 s[i-1]不能是数字
2. '-' 前面的一位 s[i-1]不能是字符')'
3. 当 '-' 处于s[0] 处即i==0,前面不可能有字符了,是负号
判断逻辑如下,news为新字符串
if(s[i]=='-'&&(i==0||(!isdigit(s[i-1])&&s[i-1]!=')')))
{
news+="0-";
}
else{
news+=s[i];
}
代码实现
static map<char, int> OperatorPriority = { {'+',1},{'-',1},{'*',2},{'/',2} };
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for (auto e : tokens) {
if (!(e == "+" || e == "-" || e == "*" || e == "/")) {
{
cout<<e;
st.push(stoi(e));
} // stoi//将字符串转化为整数
} else {
int sum1 = st.top();
st.pop();
int sum2 = st.top();
st.pop();
if (e == "+") {
st.push(sum1 + sum2);
} else if (e == "-") {
st.push(sum2 - sum1);
} else if (e == "*") {
st.push(sum2 * sum1);
} else if (e == "/") {
st.push(sum2 / sum1);
}
}
}
return st.top();
}
struct priority {
char _op;
int _pd;
};
int operatorpriority(char c)
{
struct priority
{
char _op;
int _pd;
};
static priority p[] = { {'+',1},{'-',1},{'*',2},{'/',2} };
for (auto e : p)
{
if (e._op == c)
{
return e._pd;
}
}
return -1;
}
//static map<char, int> OperatorPriority = { {'+',1},{'-',1},{'*',2},{'/',2} };//不能在这,不然需要将类实例化
class conversion
{
public:
//中缀表达式转后缀表达式
void toRPN(const string& s, size_t& i, vector<string>& v)
{
stack<char> st;
while (i < s.size())
{
if (isdigit(s[i]))//isdigit:若是数字0到九就返回true
{
string tmp;
while (i < s.size() && isdigit(s[i]))
{
tmp += s[i];
i++;
}
v.push_back(tmp);
}
else
{
if (s[i] == '(')
{
i++;
toRPN(s, i, v);
}
else if (s[i] == ')')
{
while (!st.empty())
{
v.push_back(string(1, st.top()));
st.pop();
}
i++;
return ;
}
else if (st.empty() || OperatorPriority[s[i]] > OperatorPriority[st.top()])//在上面两个判断下,不然(或)就会入栈
{
st.push(s[i]);
i++;
}
else
{
v.push_back(string(1, st.top()));
st.pop();
}
}
}
while (!st.empty())
{
v.push_back(string(1, st.top()));
st.pop();
}
}
};
int calculate(string s)
{
string news;
for(auto e:s)
{
if(e!=' ')
news+=e;
}
s.swap(news);
news.clear();
for(size_t i=0;i<s.size();i++)
{
if(s[i]=='-'&&(i==0||(!isdigit(s[i-1])&&s[i-1]!=')')))
{
news+="0-";
}
else{
news+=s[i];
}
}
vector<string> v;
conversion c;
size_t i=0;
c.toRPN(news,i,v);
return evalRPN(v);
}
};
这篇就到这里啦(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤
ヾ( ̄▽ ̄)Bye~Bye~