开头
思路和过程都在代码注释中,恕不单独解释。(挖个坑,以后来补过程的解释)
源代码
本代码中所有运算均采用 long double 类型。
全局头文件引入及类型声明
#include <cctype>
#include <cmath>
#include <cstddef>
#include <iostream>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long LL;
typedef long double LD;
中缀表达式转后缀表达式
typedef long double LD;
/**
* @brief 将中缀表达式转换为后缀表达式
*
* @param str_1 待转换的中缀表达式
* @return 转换完成的后缀表达式
*/
string infix_to_suff(string str_1)
{
// 返回运算符x的优先级
auto get_prio = [](char x)
{
// 加减法优先级最低
if (x == '+' || x == '-') return 1;
// 乘除法其次
if (x == '*' || x == '/') return 2;
// 乘方优先级最高
if (x == '^') return 3;
// 将括号设为最低优先级,这样括号除非遇到右括号,否则不会被弹出
if (x == '(' || x == ')') return 0;
// 不是以上符号返回-1
return -1;
};
// 对str_1中的负号做处理,在负号前面加上0,便于后缀计算
// 例如"-2+3"->"0-2+3", "2*(-3)"->"2*(0-3)"
string str = "";
for (size_t idx = 0; idx < str_1.size(); idx ++)
{
// u表示当前遍历的字符
char u = str_1[idx];
// 如果负号在最开头 例如-2+3
if (u == '-' && idx == 0)
{
// char类型的u转string不能用to_string(),这样转换的是字符的ASCLL码值 例如to_string('a')="97"
// 可以用构造函数string(num,c)加入num个c字符
// s.append(num,c),在s后插入num个c字符
// 也可以用stringstream类
str += "0" + string(1, u); // 在负号前面添上一个0
}
// 如果负号前面是括号 例如3*(-2)
else if (u == '-' && str_1[idx - 1] == '(')
{
str += "0" + string(1, u); // 在负号前面添上一个0
}
// 否则正常处理
else
{
str += u;
}
}
stack<char> st; // 运算符栈
string suff = ""; // 存储结果的后缀表达式
for (size_t idx = 0; idx < str.size(); idx ++)
{
// 遇到数字和小数点直接输出
// 完整接收一个运算数
while (isdigit(str[idx]) || str[idx] == '.') // 是数字或小数点就一直往后取
{
suff += str[idx];
++ idx;
// 注意下标越界问题
if (idx >= str.size()) break;
}
// 如果此时已经遍历完了直接退出
if (idx >= str.size()) break;
// 根据接下来的符号做对应操作
switch (str[idx])
{
// 遇到左括号直接入栈
case '(':
st.push(str[idx]);
break;
// 遇到右括号一直出栈,直到栈顶为左括号停止
case ')':
// 要考虑栈空的问题
while (!st.empty() && st.top() != '(')
{
suff += st.top();
st.pop();
}
// 左括号要出栈,但不送入后缀表达式
st.pop();
break;
// 字符为运算符
case '+':
case '-':
case '*':
case '/':
case '^':
// 在前面输出的数字字符后加上空格,和其他数字区别开
// 如果当前运算符是括号的话不要加空格,输出suff时会发现空格多了
suff += ' ';
// 若栈空直接入栈
if (st.empty())
{
st.push(str[idx]);
}
else
{
// 若栈非空,判断栈顶操作符,若栈顶操作符优先级低于该操作符,该操作符入栈;
if (get_prio(st.top()) < get_prio(str[idx]))
{
st.push(str[idx]);
}
// 否则一直出栈,并将出栈字符依次送入后缀表达式,直到栈空或栈顶操作符优先级低于该操作符,该操作符再入栈。
else
{
// 注意栈空的问题 如果操作符级别相同也要弹栈
while (!st.empty() && get_prio(st.top()) >= get_prio(str[idx]))
{
// 注意乘方是从右向左结合的,所以如果栈顶和当前字符都是乘方的话无需弹栈
if (str[idx] == '^' && st.top() == '^')
{
break;
}
suff += st.top();
st.pop();
}
// 最后把当前的符号入栈
st.push(str[idx]);
}
}
break;
}
}
// 接着判断符号栈是否为空,非空则把所有剩余符号出栈,并将出栈字符依次送入后缀表达式
while (!st.empty())
{
suff += st.top();
st.pop();
}
// 返回处理完的后缀表达式
return suff;
}
后缀表达式计算
typedef long double LD;
/**
* @brief 计算后缀表达式的值
*
* @param str 待计算的后缀表达式
* @return 计算结果,为long double类型
*/
LD calc_suff(string str)
{
// 判断s是不是运算符
auto is_oper = [](string s)
{
return s == "+" || s == "-" || s == "*" || s == "/" || s == "^";
};
// 把后缀表达式提取为数组 例如"6 7 +15 *"提取为["6","7","+","15","*"]
vector<string> token;
for (size_t idx = 0; idx < str.size(); )
{
// 是数字的话就提取出来,包括小数点
if (isdigit(str[idx]))
{
string temp = "";
// 提取数字
while (isdigit(str[idx]) || str[idx] == '.')
{
temp += str[idx];
++ idx;
// 注意数组下标越界问题
if (idx >= str.size()) break;
}
token.push_back(temp);
}
// 提取运算符号
else if (is_oper( string(1, str[idx]) ))
{
// char转string不能用to_string(),这样转换的是字符的ASCLL码值 例如to_string('a')="97"
// 可以用构造函数string(num,c)加入num个c字符
// s.append(num,c),在s后插入num个c字符
// 也可以用stringstream类
token.push_back(string(1, str[idx]));
++ idx;
}
// 对其他符号不做处理
else
{
++ idx; // 别忘了下标要加一
}
}
// 进行后缀表达式运算
stack<LD> st; // 存放操作数的栈
for (const auto &it : token)
{
if (is_oper(it))
{
// 先出栈的在操作符的右边
LD right = st.top(); st.pop();
LD left = st.top(); st.pop();
// 进行对应运算
if (it == "+") st.push(left + right);
else if (it == "-") st.push(left - right);
else if (it == "*") st.push(left * right);
else if (it == "/") st.push(left / right);
else if (it == "^") st.push(powl(left, right)); // powl针对long double进行乘方
}
else
{
// 对于操作数就入栈
st.push(stold(it)); // 转换为long double
}
}
return st.top();
}
计算中缀表达式(可带括号、有负数)
需结合之前的两个函数。
/**
* @brief 计算中缀表达式str的值
* @param str 待求值中缀表达式
* @return str的值
*/
LD calc_infix(string str)
{
// 中缀转后缀
string temp = infix_to_suff(str);
cout << temp << endl;
// 计算后缀表达式
LD ans = calc_suff(temp);
// 返回答案
return ans;
}
完整代码
#include <cctype>
#include <cmath>
#include <cstddef>
#include <iostream>
#include <stack>
#include <string>
#include <vector>
using namespace std;
typedef long long LL;
typedef long double LD;
/**
* @brief 将中缀表达式转换为后缀表达式
*
* @param str_1 待转换的中缀表达式
* @return 转换完成的后缀表达式
*/
string infix_to_suff(string str_1)
{
// 返回运算符x的优先级
auto get_prio = [](char x)
{
// 加减法优先级最低
if (x == '+' || x == '-') return 1;
// 乘除法其次
if (x == '*' || x == '/') return 2;
// 乘方优先级最高
if (x == '^') return 3;
// 将括号设为最低优先级,这样括号除非遇到右括号,否则不会被弹出
if (x == '(' || x == ')') return 0;
// 不是以上符号返回-1
return -1;
};
// 对str_1中的负号做处理,在负号前面加上0,便于后缀计算
// 例如"-2+3"->"0-2+3", "2*(-3)"->"2*(0-3)"
string str = "";
for (size_t idx = 0; idx < str_1.size(); idx ++)
{
// u表示当前遍历的字符
char u = str_1[idx];
// 如果负号在最开头 例如-2+3
if (u == '-' && idx == 0)
{
// char类型的u转string不能用to_string(),这样转换的是字符的ASCLL码值 例如to_string('a')="97"
// 可以用构造函数string(num,c)加入num个c字符
// s.append(num,c),在s后插入num个c字符
// 也可以用stringstream类
str += "0" + string(1, u); // 在负号前面添上一个0
}
// 如果负号前面是括号 例如3*(-2)
else if (u == '-' && str_1[idx - 1] == '(')
{
str += "0" + string(1, u); // 在负号前面添上一个0
}
// 否则正常处理
else
{
str += u;
}
}
stack<char> st; // 运算符栈
string suff = ""; // 存储结果的后缀表达式
for (size_t idx = 0; idx < str.size(); idx ++)
{
// 遇到数字和小数点直接输出
// 完整接收一个运算数
while (isdigit(str[idx]) || str[idx] == '.') // 是数字或小数点就一直往后取
{
suff += str[idx];
++ idx;
// 注意下标越界问题
if (idx >= str.size()) break;
}
// 如果此时已经遍历完了直接退出
if (idx >= str.size()) break;
// 根据接下来的符号做对应操作
switch (str[idx])
{
// 遇到左括号直接入栈
case '(':
st.push(str[idx]);
break;
// 遇到右括号一直出栈,直到栈顶为左括号停止
case ')':
// 要考虑栈空的问题
while (!st.empty() && st.top() != '(')
{
suff += st.top();
st.pop();
}
// 左括号要出栈,但不送入后缀表达式
st.pop();
break;
// 字符为运算符
case '+':
case '-':
case '*':
case '/':
case '^':
// 在前面输出的数字字符后加上空格,和其他数字区别开
// 如果当前运算符是括号的话不要加空格,输出suff时会发现空格多了
suff += ' ';
// 若栈空直接入栈
if (st.empty())
{
st.push(str[idx]);
}
else
{
// 若栈非空,判断栈顶操作符,若栈顶操作符优先级低于该操作符,该操作符入栈;
if (get_prio(st.top()) < get_prio(str[idx]))
{
st.push(str[idx]);
}
// 否则一直出栈,并将出栈字符依次送入后缀表达式,直到栈空或栈顶操作符优先级低于该操作符,该操作符再入栈。
else
{
// 注意栈空的问题 如果操作符级别相同也要弹栈
while (!st.empty() && get_prio(st.top()) >= get_prio(str[idx]))
{
// 注意乘方是从右向左结合的,所以如果栈顶和当前字符都是乘方的话无需弹栈
if (str[idx] == '^' && st.top() == '^')
{
break;
}
suff += st.top();
st.pop();
}
// 最后把当前的符号入栈
st.push(str[idx]);
}
}
break;
}
}
// 接着判断符号栈是否为空,非空则把所有剩余符号出栈,并将出栈字符依次送入后缀表达式
while (!st.empty())
{
suff += st.top();
st.pop();
}
// 返回处理完的后缀表达式
return suff;
}
/**
* @brief 计算后缀表达式的值
*
* @param str 待计算的后缀表达式
* @return 计算结果,为long double类型
*/
LD calc_suff(string str)
{
// 判断s是不是运算符
auto is_oper = [](string s)
{
return s == "+" || s == "-" || s == "*" || s == "/" || s == "^";
};
// 把后缀表达式提取为数组 例如"6 7 +15 *"提取为["6","7","+","15","*"]
vector<string> token;
for (size_t idx = 0; idx < str.size(); )
{
// 是数字的话就提取出来,包括小数点
if (isdigit(str[idx]))
{
string temp = "";
// 提取数字
while (isdigit(str[idx]) || str[idx] == '.')
{
temp += str[idx];
++ idx;
// 注意数组下标越界问题
if (idx >= str.size()) break;
}
token.push_back(temp);
}
// 提取运算符号
else if (is_oper( string(1, str[idx]) ))
{
// char转string不能用to_string(),这样转换的是字符的ASCLL码值 例如to_string('a')="97"
// 可以用构造函数string(num,c)加入num个c字符
// s.append(num,c),在s后插入num个c字符
// 也可以用stringstream类
token.push_back(string(1, str[idx]));
++ idx;
}
// 对其他符号不做处理
else
{
++ idx; // 别忘了下标要加一
}
}
// 进行后缀表达式运算
stack<LD> st; // 存放操作数的栈
for (const auto &it : token)
{
if (is_oper(it))
{
// 先出栈的在操作符的右边
LD right = st.top(); st.pop();
LD left = st.top(); st.pop();
// 进行对应运算
if (it == "+") st.push(left + right);
else if (it == "-") st.push(left - right);
else if (it == "*") st.push(left * right);
else if (it == "/") st.push(left / right);
else if (it == "^") st.push(powl(left, right)); // powl针对long double进行乘方
}
else
{
// 对于操作数就入栈
st.push(stold(it)); // 转换为long double
}
}
return st.top();
}
/**
* @brief 计算中缀表达式str的值
* @param str 待求值中缀表达式
* @return str的值
*/
LD calc_infix(string str)
{
// 中缀转后缀
string temp = infix_to_suff(str);
cout << temp << endl;
// 计算后缀表达式
LD ans = calc_suff(temp);
// 返回答案
return ans;
}
int main()
{
string str;
getline(cin, str);
cout << calc_infix(str) << endl;
return 0;
}
结尾
本人才疏学浅,有错误欢迎指正!