C++中、后缀表达式间的转换及运算方法(附代码,可带括号、小数和负数)

开头

思路和过程都在代码注释中,恕不单独解释。(挖个坑,以后来补过程的解释)

源代码

本代码中所有运算均采用 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;
}

结尾

本人才疏学浅,有错误欢迎指正!

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我来为你写一个把含小数负数缀表达式转换后缀表达式的函数。 思路如下: 1. 定义一个栈,保存运算符和左括号。 2. 遍历缀表达式的每个元素。 3. 如果是数字,直接输出。 4. 如果是左括号,入栈。 5. 如果是右括号,将栈运算符弹出并输出,直到遇到左括号。 6. 如果是运算符,弹出栈所有优先级大于或等于该运算符的运算符,并输出,然后将该运算符入栈。 7. 遍历完整个表达式后,将栈所有运算符弹出并输出。 以下是代码实现: ```c++ #include <iostream> #include <stack> #include <string> #include <sstream> #include <cmath> using namespace std; // 判断是否是运算符 bool isOperator(char c) { return (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')'); } // 比较运算符优先级 int priority(char op) { if (op == '+' || op == '-') { return 1; } else if (op == '*' || op == '/') { return 2; } else { return 0; } } // 将缀表达式转换后缀表达式 string infixToPostfix(string infix) { stack<char> s; stringstream ss; char c; for (int i = 0; i < infix.length(); i++) { c = infix[i]; // 是数字,直接输出 if (isdigit(c) || c == '.') { ss << c; } // 是运算符 else if (isOperator(c)) { // 如果是负数,则加上一个0 if (c == '-' && (i == 0 || isOperator(infix[i - 1]))) { ss << "0 "; } // 如果是左括号,入栈 else if (c == '(') { s.push(c); } // 如果是右括号,弹出栈运算符并输出,直到遇到左括号 else if (c == ')') { while (s.top() != '(') { ss << s.top() << " "; s.pop(); } s.pop(); } // 如果是其他运算符,弹出栈所有优先级大于或等于该运算符的运算符,并输出,然后将该运算符入栈 else { while (!s.empty() && s.top() != '(' && priority(s.top()) >= priority(c)) { ss << s.top() << " "; s.pop(); } s.push(c); } } } while (!s.empty()) { ss << s.top() << " "; s.pop(); } return ss.str(); } int main() { string infix = "2.5+(3-1)*4-3/2"; string postfix = infixToPostfix(infix); cout << postfix << endl; // 输出:2.5 3 1 - 4 * + 3 2 / - return 0; } ``` 希望能帮助到你。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值