字符串的四则运算,可以将字符串表示的中缀表达式转换为后缀表达式(逆波兰表示法, Reverse Polish Notation),然后对后缀表达式进行求解。
中缀->后缀:
利用栈来处理操作符的优先顺序。具体为在中缀表达式中如果遇到了数字,则直接输出,如果遇到了操作符s
,则将其与栈中的操作符比对,如果该操作符的优先级小于等于栈顶操作符的优先级,则将栈顶中的操作符依次出栈输出,直到栈顶的操作符小于该操作符s
,并将其入栈。如果遇到了右括号,则将栈中元素弹出直到遇到左括号。
后缀表达式求值:
将数字直接压入栈中,如果遇到操作符,则将栈顶num1
和次栈顶num2
依次弹出,对num2
和num1
进行四则运算,并将结果压入栈中,重复以上步骤,直到后缀表达式中的各个元素都处理过了。
#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
#include<sstream>
#include<stack>
using namespace std;
// 中缀表达式转后缀表达式,返回的表达式中用不定数的空格将操作数(或操作符)和操作符(或操作数)分开
string getRPN(string s) {
stack<char> sk;
string res;
int n = s.size();
for (int i = 0; i < n; ++i) {
while (i < n && isdigit(s[i])) {
res += s[i++];
}
res += ' ';
if (i == n) break;
if (s[i] == '+' || s[i] == '-') {
// 将栈顶中所有优先级大于等于'+','-'的操作符依次弹出,最后将当前操作符入栈
while (!sk.empty()) {
if (sk.top() == '+' || sk.top() == '-' || sk.top() == '*' || sk.top() == '/') {
res += ' '; // 先加空格
res += sk.top();
sk.pop();
}
else
break;
}
sk.push(s[i]);
}
else if (s[i] == '*' || s[i] == '/') {
while (!sk.empty()) {
if (sk.top() == '*' || sk.top() == '/') {
res += ' ';
res += sk.top();
sk.pop();
}
else
break;
}
sk.push(s[i]);
}
else if (s[i] == '(') {
sk.push('(');
}
else if (s[i] == ')') {
// 将配对左括号之间的全部操作符依次输出
while (!sk.empty() && sk.top() != '(') {
res += ' ';
res += sk.top();
sk.pop();
}
sk.pop(); // 将左括号弹出
}
else
continue; // 跳过空格之类的
}
// 将栈中剩余的操作符依次全部弹出
while (!sk.empty()) {
res += ' ';
res += sk.top();
sk.pop();
}
return res;
}
// 计算后缀表达式的值
int calStr(const string& src) {
stringstream ss(src);
string s;
stack<int> sk;
int res = 0;
while (ss >> s) {
// 如果遇到数字,则压入栈中
if (isdigit(s[0])) {
sk.push(stoi(s));
}
else {
// 如果遇到操作符,则弹出栈顶的两个元素,计算后将结果再入栈
int num1 = sk.top(); sk.pop();
int num2 = sk.top(); sk.pop();
if (s == "+") res = num2 + num1;
if (s == "-") res = num2 - num1;
if (s == "*") res = num2 * num1;
if (s == "/") res = num2 / num1;
sk.push(res);
}
}
return res;
}
int main()
{
string s = "90 - 30 + ((220 * (12 + (333 - 19) / 3)) + 498) * 31 + 108 / 3";
string rpn_s = getRPN(s);
cout << calStr(rpn_s) << endl;
cout << 90 - 30 + ((220 * (12 + (333 - 19) / 3)) + 498) * 31 + 108 / 3 << endl;
return 0;
}
90 30 - 220 12 333 19 - 3 / + * 498 + 31 * + 108 3 / +
806654
806654
解法2:
int calculate(string s) {
int res = 0, cur_res = 0, num = 0;
char op = '+';
s += '+';
for (int i = 0; i < s.size(); ++i) {
char c = s[i];
if (isdigit(c))
num = num * 10 + c - '0';
else if (c == '(') {
// 将括号中的子字符串作为一个表达式
int left = i;
int cnt = 0; // 保证括号能够匹对的上
for (; i < s.size(); ++i) {
if (s[i] == '(') ++cnt;
if (s[i] == ')') --cnt;
if (cnt == 0) break;
}
num = calculate(s.substr(left + 1, i - left + 1 - 2));
}
else if (c == '+' || c == '-' || c == '*' || c == '/') {
switch (op) // 使用的是op,不是c
{
case '+': cur_res += num; break;
case '-': cur_res -= num; break;
case '*': cur_res *= num; break;
case '/': cur_res /= num; break;
}
if (c == '+' || c == '-') {
res += cur_res;
cur_res = 0;
}
op = c;
num = 0;
}
}
return res;
}