题目链接:
https://ac.nowcoder.com/acm/problem/50999
题面:
给出一个表达式,其中运算符仅包含+,-,*,/,^(加 减 乘 整除 乘方)要求求出表达式的最终值
数据可能会出现括号情况,还有可能出现多余括号情况
数据保证不会出现\geq 2^{31}的答案数据保证不会出现 ≥ 2^31 的答案
数据可能会出现负数情况
递归方案解答(完美AC):
#include<iostream>
#include<cstring>
#include<math.h>
#include<algorithm>
using namespace std;
// 递归实现
string s;
int nums(int l, int r) {
int cnt = 0;
for (int i = l; i <= r; i++)
cnt = cnt * 10 + (s[i] - '0');
return cnt;
}
int calc(int l, int r) {
if (l > r) return 0;
int pos1 = -1, pos2 = -1, pos3 = -1; // ops1 : +- ; pos2 : */ ; pos3 : ^
int cnt = 0; // 标记左右括号
for (int i = l; i <= r; i++) {
// 统计当前范围内的括号个数
if (s[i] == '(') cnt++;
if (s[i] == ')') cnt--;
// 定位这个范围内相应运算符号最后一次出现的地点(越早被定位,递归深度越浅,越晚被执行运算)
// 这里是从递归角度出发确定相同优先级的运算符的执行顺序
if ((s[i] == '+' || s[i] == '-') && cnt == 0) pos1 = i;
if ((s[i] == '*' || s[i] == '/') && cnt == 0) pos2 = i;
if (s[i] == '^' && cnt == 0) pos3 = i;
}
// 在当前范围内没有符号的情况下,进行条件判断
if (pos1 == -1 && pos2 == -1 && pos3 == -1) {
//// 多余括号情况
// 左括号多余
if (cnt > 0 && s[l] == '(') return calc(l+1, r);
// 右括号多余
else if (cnt < 0 && s[r] == ')') return calc(l, r-1);
//// cnt == 0, 说明要么是数字,要么有一对括号括住它
else if (cnt == 0 && s[l] == '(' && s[r] == ')') return calc(l+1, r-1);
else return nums(l, r);
}
// 按优先级递归
if (pos1 != -1) { // + - 优先级最低
if (s[pos1] == '+') return calc(l, pos1-1) + calc(pos1+1, r);
if (s[pos1] == '-') return calc(l, pos1-1) - calc(pos1+1, r);
}
else if (pos2 != -1) { // * / 优先级中等
if (s[pos2] == '*') return calc(l, pos2-1) * calc(pos2+1, r);
if (s[pos2] == '/') return calc(l, pos2-1) / calc(pos2+1, r);
}
else if (pos3 != -1) { // ^ 优先级最高
return (int)pow(calc(l, pos3-1), calc(pos3+1, r));
}
return 0; // 编译器版本问题,需要加上这句
}
int main () {
cin >> s;
cout << calc(0, s.length()-1);
}
(非递归)栈方案解答,要考虑的情况多(不太完美,样例数据强度不行,但是AC):
先利用两个栈(符号栈,结果栈)把输入的中缀表达式转成后缀表达式,再利用一个数据缓存栈计算后缀表达式。
中缀表达式转后缀表达式的常规思路归纳整理
(1) 初始化两个栈:运算符栈S1和储存中间结果的栈S2;
(2) 从左至右扫描中缀表达式;
(3) 遇到操作数时,将其压入S2;
(4) 遇到运算符时,比较其与S1栈顶运算符的优先级:
(4-1) 如果S1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
(4-2) 否则,若优先级比栈顶运算符的高,也将运算符压入S1(注意转换为前缀表达式时是优先级较高或相同,而这里则不包括相同的情况);
(4-3) 否则,将S1栈顶的运算符弹出并压入到S2中,再次转到(4-1)与S1中新的栈顶运算符相比较;
(5) 遇到括号时:
(5-1) 如果是左括号“(”,则直接压入S1;
(5-2) 如果是右括号“)”,则依次弹出S1栈顶的运算符,并压入S2,直到遇到左括号为止,此时将这一对括号丢弃;
(6) 重复步骤(2)至(5),直到表达式的最右边;
(7) 将S1中剩余的运算符依次弹出并压入S2;
(8) 依次弹出S2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式(转换为前缀表达式时不用逆序)。
代码草稿(有待优化):
#include<iostream>
#include<cstring>
#include<stack>
#include<map>
#include<vector>
#include<algorithm>
#include<functional>
using namespace std;
map<char, int> op = {
{'+', 1}, {'-', 1},
{'*', 2}, {'/', 2}, {'^', 3},
{'(', 0}, {')', 0}
};
/*
注意点1:
数字大小,要考虑十位百位千位的数据之间运算
注意点2:
数据可能会出现括号情况,还有可能出现多余括号情况
举例:
(((((1+4*5)此时左括号可以删去
(1+4*5))))))右括号可以删去
注意点3:
数据可能会有负数的情况
举例:
样例输入:
-123
(-123)
((-123)
(-123))))
-(123)
1+(-2)
1+(-2)*2+3
1+((-2)*2+3
1+(-2)*2+3
本题目前不太会用栈来实现
因为括号的问题和minus负数的问题
即使是正确的表达式,虽然题目是AC了,但是这是样例的强度不够
(-((-2+(-2))))^(1+1)
本代码没有考虑括号对齐的情况
*/
int main () {
string s;
cin >> s;
// 中缀表达式转后缀表达式,同时去除/清理括号
stack<char> op_st;
stack<string> hou_st;
int flag = 0;
int ptr_l_kuohao[35] = {}; //
for (int i = 0; i < s.length(); i++) {
// s[i] 是数字
if (s[i] >= '0' && s[i] <= '9') {
string nums;
while (s[i] >= '0' && s[i] <= '9' && i < s.length()) {
nums.push_back(s[i++]);
}
i--;
hou_st.push(nums);
// cout << "i : " << i << " nums : " << nums << endl;
}
else { // s[i] 是符号
if (s[i] == '(') { // 要排除孤儿左括号的影响
int ptr_r = i+1;
while(ptr_r < s.length()) { // 查询是否有和当前左括号匹配的最近的右括号,注意不要和后面的左括号冲突
if (s[ptr_r] == '(') {
ptr_r = s.length();
break;
}
if (s[ptr_r] == ')' && ptr_l_kuohao[ptr_r] == 0) {
ptr_l_kuohao[ptr_r] = 1;
break;
}
ptr_r++;
}
if (ptr_r != s.length()) { // 符号栈只存储非孤儿的左括号
op_st.push('(');
flag++;
}
cout << "i : " << i << " 102 first op : " << s[i] << endl;
}
else if (op_st.empty()) { // 符号栈为空
op_st.push(s[i]);
// cout << "i : " << i << " 107 op : " << s[i] << endl;
}
else if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '^') {
if (s[i] == '-') {
if (s[i-1] == '(' || i == 0) { // 判定是否为负数 minus
if (s[i+1] >= '0' && s[i+1] <= '9') {
i++;
string nums;
nums.append("-");
while (s[i] >= '0' && s[i] <= '9' && i < s.length()) {
nums.push_back(s[i++]);
}
i--;
hou_st.push(nums);
// cout << "i : " << i << " 123 op-nums : " << nums << endl;
}
}
if (s[i+1] == ')') {
op_st.pop();
i++;
flag--;
continue;
}
}
// 当前访问的运算符优先级大于符号栈顶的,把当前访问的存入符号栈顶
if (op[s[i]] > op[op_st.top()]) {
op_st.push(s[i]);
cout << "i : " << i << " 142 op push : " << s[i] << endl;
}
// 当前访问的运算符优先级小于等于符号栈栈顶,
else if (op[s[i]] <= op[op_st.top()]) {
while (!op_st.empty() && op[s[i]] <= op[op_st.top()]) {
if (op_st.top() != '(') {
string opera;
opera.push_back(op_st.top());
hou_st.push(opera);
// cout << "i : " << i << " after pop <= next op : " << s[i] << endl;
}
op_st.pop();
}
op_st.push(s[i]);
}
}
else if (s[i] == ')' && flag > 0) { // 有左括号 使flag-- 才能有右括号执行下面的代码
while(op_st.top() != '('){
string opera;
opera.push_back(op_st.top());
hou_st.push(opera);
op_st.pop();
// cout << "i : " << i << " ( flag : " << flag << endl;
}
if (!op_st.empty()) { // 防止孤儿左括号(此处可以不用)
flag--;
op_st.pop(); // 把符号栈中的左括号弹出
}
}
}
// cout << "i : " << i << " ";
// cout << "打印 op_st : ";
// stack<char> tmp_op_st = op_st;
// while (!tmp_op_st.empty()) {
// cout << tmp_op_st.top() << " ";
// tmp_op_st.pop();
// }
//
// cout << endl << "打印 hou_st : " << endl;
// stack<string> tmp_hou_st = hou_st;
// while (!tmp_hou_st.empty()) {
// cout << tmp_hou_st.top() << " ";
// tmp_hou_st.pop();
// }
//
// cout << endl;
}
while(!op_st.empty()) {
if (op_st.top() != '(' || op_st.top() != ')') {
string remain;
remain.push_back(op_st.top());
hou_st.push(remain);
}
op_st.pop();
}
vector<string> hou;
while (!hou_st.empty()) {
// if (hou_st.top() != "(" || hou_st.top() != ")") {
hou.push_back(hou_st.top());
// }
hou_st.pop();
}
reverse(hou.begin(), hou.end());
// cout << endl << endl;
// cout << "中缀转后缀:"<< endl;
// for (auto a : hou) {
// cout << a << " ";
// }
// cout << endl << endl;
// 测试数据
// 2+2^4/8-6 --> 234^/8-6 --> -2
// (2+2)^(1+1) --> 22+11+^ --> 16
// 3+(2*5+7^2)-6*(3+2) --> 32
// 123+444*2^2/111-24 --> 115
// 100-5*5^(1+2)/5+25 --> 0
// 后缀表达式入栈计算
// 只要(数字+中间结果)栈 即可
stack<long long> ans_st;
function<long long(string)> calc = [&](string nums) {
long long res = 0;
if (nums[0] != '-') {
for (int i = 0; i < nums.length(); i++) {
res = res * 10 + (nums[i] - '0');
}
}
else {
for (int i = 1; i < nums.length(); i++) {
res = res * 10 + (nums[i] - '0');
}
res = -res;
}
return res;
};
for (int i = 0; i < hou.size(); i++) {
if (hou[i] == "(" || hou[i] == ")") continue;
if (hou[i][0] >= '0' && hou[i][0] <= '9' || hou[i].length() >= 2) {
ans_st.push(calc(hou[i]));
}
else { // 五种符号
long long l = 0, r = 0, tmp = 0;
r = ans_st.top();
ans_st.pop();
if (!ans_st.empty()) { // 防范 负数样例 -123
l = ans_st.top();
ans_st.pop();
}
if (hou[i][0] == '+') {
tmp = l + r;
}
else if (hou[i][0] == '-') {
tmp = l - r;
}
else if (hou[i][0] == '*') {
tmp = l * r;
}
else if (hou[i][0] == '/') {
tmp = l / r;
}
else if (hou[i][0] == '^') {
tmp = 1;
for (int j = 0; j < r; j++) {
tmp *= l;
}
}
ans_st.push(tmp);
// cout << hou[i] << " : ";
// // 状态打印
// stack<long long> tmp_ans_st = ans_st;
// while(!tmp_ans_st.empty()){
// cout << tmp_ans_st.top() << " ";
// tmp_ans_st.pop();
// }
// cout << endl;
}
}
cout << ans_st.top();
}