Evaluate Reverse Polish Notation

本文详细介绍了如何使用栈来解决逆波兰表达式求值问题,并提供了代码实现的步骤和逻辑,帮助读者理解并掌握逆波兰表达式的基本概念和求值方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

仅提供个人的一种解题思路,未必是最优,仅供各位参考!

import java.util.Stack;

/**
 * 
 * <p>
 * ClassName SolutionEvaluateReversePolishNotation
 * </p>
 * <p>
 * Description 该题是解逆波兰表达式 Evaluate the value of an arithmetic expression in Reverse Polish Notation.
 * 
 * Valid operators are +, -, *, /. Each operand may be an integer or another expression.
 * 
 * Some examples: ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9 ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6
 * </p>
 * 
 * @author wangxu wangx89@126.com
 *         <p>
 *         Date 2014-9-11 下午08:11:44
 *         </p>
 * @version V1.0
 * 
 */
public class SolutionEvaluateReversePolishNotation {
	public static int evalRPN(String[] tokens) {
		// 基本思路是采用栈来存储数字,然后读取token,当读取到的是+-*/时候,进行出栈操作

		Stack<Integer> numStack = new Stack<Integer>();// 存储数字的栈
		Stack<String> opStack = new Stack<String>();// 存储操作符的栈
		int length = tokens.length;
		String op = "";
		for (int i = 0; i < length; i++) {

			String temp = tokens[i];
			if ("+".equals(temp)) {
				if (i < length - 1) {// 说明当前操作符并非最后一个
					op = tokens[i + 1];// 取出当前操作符的后一个,比较他们的优先级,如果后一个比当前优先级高,那么当前操作符入栈,如果后一个没有当前优先级高,那么计算当前的操作符
					if ("*".equals(op) || "/".equals(op)) {// 只有当后面一个是*或者/时,将当前操作符入栈
						opStack.push("+");
					} else {
						// 说明可能是+或者-或者数字,但不影响运算顺序
						// 直接使用当前操作符计算即可
						int num1 = numStack.pop();
						int num2 = numStack.pop();
						numStack.push(num2 + num1);

					}
				} else {// 说明已经是最后一个操作符了
					int num1 = numStack.pop();
					int num2 = numStack.pop();
					numStack.push(num2 + num1);

				}
			} else if ("-".equals(temp)) {

				if (i < length - 1) {
					op = tokens[i + 1];
					if ("*".equals(op) || "/".equals(op)) {
						opStack.push("-");
					} else {
						// 直接使用当前操作符计算即可
						int num1 = numStack.pop();
						int num2 = numStack.pop();
						numStack.push(num2 - num1);

					}
				} else {
					int num1 = numStack.pop();
					int num2 = numStack.pop();
					numStack.push(num2 - num1);
				}

			} else if ("*".equals(temp)) {

				int num1 = numStack.pop();
				int num2 = numStack.pop();
				numStack.push(num2 * num1);

			} else if ("/".equals(temp)) {

				int num1 = numStack.pop();
				int num2 = numStack.pop();
				numStack.push(num2 / num1);

			} else {
				int t = Integer.parseInt(temp);
				numStack.push(t);
			}

			// 开始判断操作符栈,如果操作符栈非空,那么需要先将其出栈操作
			while (!opStack.isEmpty()) {
				String opTemp = opStack.pop();
				if ("+".equals(opTemp)) {
					int num1 = numStack.pop();
					int num2 = numStack.pop();
					numStack.push(num2 + num1);
				} else if ("-".equals(opTemp)) {
					int num1 = numStack.pop();
					int num2 = numStack.pop();
					numStack.push(num2 - num1);
				}

			}
		}

		return numStack.pop();
	}

	public static void main(String[] args) {
		String str[] = { "-78", "-33", "196", "+", "-19", "-", "115", "+", "-", "-99", "/", "-18", "8", "*", "-86", "-", "-", "16", "/", "26", "-14", "-", "-", "47",
				"-", "101", "-", "163", "*", "143", "-", "0", "-", "171", "+", "120", "*", "-60", "+", "156", "/", "173", "/", "-24", "11", "+", "21", "/", "*", "44",
				"*", "180", "70", "-40", "-", "*", "86", "132", "-84", "+", "*", "-", "38", "/", "/", "21", "28", "/", "+", "83", "/", "-31", "156", "-", "+", "28", "/",
				"95", "-", "120", "+", "8", "*", "90", "-", "-94", "*", "-73", "/", "-62", "/", "93", "*", "196", "-", "-59", "+", "187", "-", "143", "/", "-79", "-89",
				"+", "-" };
		int res = evalRPN(str);
		System.out.println(res);

	}
}


<think>嗯,用户让我写一个C++程序来计算逆波兰表达式。那我得先回忆一下逆波兰表达式是什么。逆波兰式,也就是后缀表达式,运算符放在操作数的后面。比如“1 2 +”就是1加2。那这个程序的输入是一个字符串,里面包含数字和运算符,我需要用栈来处理这个表达式。 首先,我得考虑怎么读取输入。输入的表达式是用空格分隔的,所以可能需要用split函数把每个元素分开。但是C++标准库里没有现成的split函数,我得自己处理。可以用stringstream来逐个读取每个token,这样比较方便。 然后,处理每个token的时候,如果是数字就压入栈中,如果是运算符,就需要从栈里弹出两个操作数进行计算。这里要注意顺序,比如减法和除法的时候,第二个弹出的数是被减数或被除数。比如遇到“-”,应该是先弹出的第二个数减去第一个数。 接下来需要考虑错误情况。比如,当遇到运算符时,栈里的元素不足两个,这时候整个表达式就是无效的,应该返回INVALID。或者在处理完所有token后,栈里剩下的元素不是正好一个,那也是无效的。另外还有除法的时候,除数可能为零,这时候也要处理,返回INVALID。 那程序的步骤大概是这样的: 1. 读取输入行,分割成各个token。 2. 遍历每个token: a. 如果是数字,压入栈。 b. 如果是运算符,检查栈的大小是否至少有两个元素,不够的话无效。 c. 弹出两个操作数,进行运算。注意运算顺序和除法除零的情况。 3. 遍历结束后,检查栈的大小是否为1,否则无效。 4. 输出结果或INVALID。 现在具体实现的话,如何判断一个token是数字还是运算符?可能要看这个token是否在运算符集合里。比如遇到“+”、“-”、“*”、“/”中的一个,就是运算符,否则尝试转换为数字。如果有无法转换的情况,比如像字母之类的,那整个表达式也是无效的。 比如输入中有“1 + 2”,那分割后有三个token:1,+,2。这时候处理到第二个token“+”时,栈里只有一个元素,这时候弹不出两个,所以无效。符合样例中的第二个例子输出INVALID。 对于除法运算,C++里的整数除法是向零取整吗?例如,7除以2等于3,而-7除以2等于-3。题目里说integer division,应该就是这样的处理方式。所以在运算的时候,直接用除法操作符即可,但要注意除数不能为零。 另外,输入的整数可能有负数,比如第三个输入例子中的“-2”。这时候读取数字的时候,需要正确处理负号。可以用stoi或者istringstream来转换字符串为整数。 那代码的大致结构: - 使用一个栈,比如stack<int>。 - 逐个读取token。 - 对于每个token: if是运算符: 检查栈是否足够两个元素,否则返回invalid。 弹出两个数,进行计算。注意顺序。 检查除数是否为零。 将结果压入栈。 else: 尝试转换为整数,如果失败则invalid。 压入栈。 - 最后检查栈是否只剩一个元素,否则invalid。 需要注意错误处理的顺序。比如,当处理运算符时,栈是否足够,运算是否合法(除零)等。每一步都有可能出错,所以需要及时返回错误。 那现在写代码的时候,如何读取每个token呢?可以用stringstream来按空格分割。例如: string expr; getline(cin, expr); istringstream iss(expr); string token; while (iss >> token) { // 处理每个token } 这样就能逐个读取用空格分隔的token了。 接下来,对于每个token,判断是否是运算符。可以用一个集合或者简单的条件判断: if (token == "+" || token == "-" || token == "*" || token == "/") { // 处理运算符 } else { // 尝试转为数字 } 但是,可能存在像“123a”这样的无效token,这时候转为数字会失败。这时候需要处理错误。可以用stoi,但是stoi可能会抛出异常,或者在转换的时候检查整个字符串是否都是数字。或者自己写一个函数判断是否是有效整数。 或者,可以用strtol函数,检查是否整个字符串都被转换。例如,对于字符串s,用char* endptr; long num = strtol(s.c_str(), &endptr, 10); 然后检查endptr是否等于s.c_str() + s.size()。如果是的话,说明整个字符串被转换了。否则,说明不是有效数字。 但是在C++中,可能需要更简便的方式。比如,对于每个token,尝试转换为整数。如果转换失败,则整个表达式无效。比如: bool isNumber(const string& token) { if (token.empty()) return false; size_t pos = 0; try { int num = stoi(token, &pos); return pos == token.size(); } catch (...) { return false; } } 这样,当调用stoi时,如果转换到整个字符串,则pos等于token的长度。否则,说明转换中途停止,比如包含非数字字符。 但stoi在转换时,会自动忽略前导空格吗?比如“ 123”会被转换为123吗?不过在我们的情况下,每个token是用iss >> token读取的,所以token不会有前导或尾随空格,因为>>操作符会自动跳过空格。所以每个token要么是运算符,要么是有效的整数(包括负数),或者无效字符。 比如,假设输入是“1 a +”,那么在处理第二个token“a”的时候,转换失败,整个表达式无效。 所以在处理每个token的时候,如果是运算符则处理,否则尝试转换数字,如果转换失败则整个表达式无效。 现在,在代码中如何处理这些情况呢? 可能的步骤: 初始化一个栈。遍历每个token: for each token in tokens: if token is operator: if stack.size() < 2: invalid else: b = stack.top(); stack.pop(); a = stack.top(); stack.pop(); compute a op b if op is '/' and b ==0: invalid push result else: check if token is a valid integer if not, invalid push to stack 在处理过程中,一旦发现任何错误(如操作数不足、除零、无效token),就标记为invalid,并结束处理。 最后,如果处理完所有token,栈中只剩一个元素,输出结果,否则invalid。 那将这些逻辑转化为C++代码。需要注意的是,除法时的运算顺序。例如,当处理表达式“7 2 2 - /”,即对应的中缀是7/(2-2)?那么当运算符是“-”的时候,弹出的是2和2,得到0。然后处理“/”的时候,弹出的是0和7?或者顺序是否有问题? 比如,假设token是“-”,那么栈中有7,2,2。运算符是“-”,弹出前两个元素:2和2,运算得到0,压入栈。此时栈是7,0。然后处理“/”,弹出0和7,这时候运算7 / 0?这样会导致除零错误。而原例子中的输入“7 2 2 - /”对应的输出是INVALID,因为除以零。所以此时应该处理这种情况。 但这里可能我的逻辑有问题,因为操作数的顺序是否正确? 逆波兰式的处理顺序是,遇到运算符时,弹出两个元素,其中第一个弹出的是右操作数,第二个是左操作数。例如,对于“a b op”,对应的计算是 a op b。比如,减法的话是 a - b,除法是 a / b。所以当处理运算符时,弹出顺序是b,然后是a,所以运算顺序是 a op b。 比如,遇到“3 4 -”则计算3 -4?或者4-3? 等一下,逆波兰式的处理顺序是,当运算符作用于前面的两个数。例如,“3 4 +”是3+4。那如果是“3 4 -”,应该是3-4吗?或者4-3? 逆波兰式是操作符在操作数的后面。比如“3 4 -”对应的中缀表达式是3 -4?或者3 4的差? 不,正确的逆波兰式处理方式是,运算符前面的两个数作为操作数。例如,表达式“3 4 -”等于3 -4?或者等于4-3? 比如,正确的计算方式应该是,当遇到运算符时,弹出栈顶的两个元素,其中第一个弹出的是第二个操作数,第二个弹出的是第一个操作数。例如,对于“a b op”,计算的是a op b。比如,“3 4 -”的结果是3 -4= -1。或者,“5 2 /”是5/2=2(整数除法)。 或者是否应该反过来?比如,假设栈中是先压入a,然后压入b。当遇到op时,弹出的是b,然后是a,计算a op b。例如: 比如处理“1 2 +”,栈中依次压入1,2。遇到“+”时,弹出2和1,相加得3,压入栈。结果正确。所以运算符的处理顺序是正确的,即a和b的顺序是第一个弹出的b,第二个弹出的a,然后运算a op b。例如,对于减法,a - b,其中a是第二个弹出的数,b是第一个弹出的。 比如,表达式“5 3 -”即对应中缀表达式5 -3,结果是2。那处理时,弹出3,然后弹出5,运算5-3=2。 所以,正确的操作顺序是,当处理运算符时,先弹出的是右操作数,后弹出的是左操作数,然后运算左操作数 op 右操作数。这样,减法、除法的顺序是正确的。 所以在代码中,当处理运算符时,比如token是“-”,弹出b,再弹出a,计算a - b。 那除法的情况也是一样的,a / b,其中a是左操作数,b是右操作数。比如,栈中有7,2,2。假设处理的是“-”运算符,弹出2和2,计算2 -2=0,压入栈。此时栈是7和0。然后处理“/”运算符,弹出0和7,计算7 /0,这时候会触发除零错误,所以输出INVALID,如样例中的第四个输入所示。 所以代码中的处理顺序是正确的。 那现在把这些情况考虑进去,编写代码。 可能的代码结构: #include <iostream> #include <stack> #include <sstream> #include <string> #include <vector> using namespace std; bool isOperator(const string& token) { return token == "+" || token == "-" || token == "*" || token == "/"; } bool isNumber(const string& token) { if (token.empty()) return false; size_t pos = 0; try { // Check if the token is a valid integer int num = stoi(token, &pos); return pos == token.size(); } catch (...) { return false; } } int main() { string expr; getline(cin, expr); istringstream iss(expr); stack<int> stk; string token; bool invalid = false; while (iss >> token) { if (isOperator(token)) { if (stk.size() < 2) { invalid = true; break; } int b = stk.top(); stk.pop(); int a = stk.top(); stk.pop(); if (token == "+") { stk.push(a + b); } else if (token == "-") { stk.push(a - b); } else if (token == "*") { stk.push(a * b); } else if (token == "/") { if (b == 0) { invalid = true; break; } stk.push(a / b); } } else { if (!isNumber(token)) { invalid = true; break; } stk.push(stoi(token)); } } if (invalid || stk.size() != 1) { cout << "INVALID" << endl; } else { cout << stk.top() << endl; } return 0; } 那现在测试一下样例: 第一个输入:1 2 + 3 * 处理过程: tokens是1, 2, +, 3, * 处理1:压栈,栈:1 处理2:压栈,栈:1,2 处理+:弹出2和1,计算1+2=3。压栈3。栈:3 处理3:压栈3,栈:3,3 处理*:弹出3和3,计算3*3=9。压栈9。结束。 栈大小是1,输出9。正确。 第二个输入:1 + 2 → tokens是1,+,2. 处理1:压栈→栈:1. 处理+:此时栈大小只有1,不够弹出两个,标记invalid,退出循环。输出INVALID。正确。 第三个输入:7 -2 + 2 / → tokens是7,-2,+,2,/。这可能需要看输入是否带空格。假设输入是“7 -2 + 2 /” → tokens是7, -2, +, 2, /. 处理7:压栈→7. 处理-2:是数字,压栈→7, -2. 处理+:弹出-2和7,计算7 + (-2) =5。压栈5. 处理2:压栈→5,2. 处理/:弹出2和5,计算5/2=2。压栈2. 最后栈大小是1,输出2。正确。 第四个输入:7 2 2 - / → tokens是7,2,2,-,/. 处理7→栈7. 处理2→栈7,2. 处理2→7,2,2. 处理-:弹出2和2,计算2-2=0。栈7,0. 处理/:弹出0和7,计算7/0→此时检测到b=0,标记invalid。输出INVALID。正确。 那这样的代码应该可以处理这些情况。 但可能需要注意负数的处理,例如“-2”是否被正确识别为数字。在isNumber函数中,stoi可以正确转换负数,所以没有问题。 另外,对于像“0”或者多个零的情况,也能处理吗? 比如输入“0 0 /” → 除零错误,输出INVALID。 那这段代码应该能够正确处理这些情况。 综上,这个程序应该满足题目的要求。</think>要编写一个C++程序来评估逆波兰表达式(RPN),需遵循以下步骤: ### 问题分析 1. **逆波兰表达式特点**:运算符在操作数之后,例如 $1\ 2\ +$ 表示 $1+2$。 2. **栈的使用**:遇到数字压栈,遇到运算符弹出栈顶两个元素运算后压栈。 3. **错误处理**:操作数不足、除零、非法字符、最终栈大小不为1时均需输出 `INVALID`。 ### 实现步骤 1. **输入处理**:读取整行输入并按空格分割为 `tokens`。 2. **遍历 tokens**: - **数字处理**:验证是否为合法整数并压栈。 - **运算符处理**:检查栈是否足够两个元素,弹出并运算后压栈。 3. **异常检测**:在运算过程中实时检测错误并终止处理。 4. **结果输出**:若最终栈仅剩一个元素且无错误,输出结果;否则输出 `INVALID`。 ### 示例代码 ```cpp #include <iostream> #include <stack> #include <sstream> #include <string> using namespace std; bool isOperator(const string& token) { return token == "+" || token == "-" || token == "*" || token == "/"; } bool isNumber(const string& token) { if (token.empty()) return false; size_t pos = 0; try { stoi(token, &pos); // 尝试将字符串转为整数 return pos == token.length(); // 确保整个字符串都被转换 } catch (...) { return false; } } int main() { string expr; getline(cin, expr); // 读取整行输入 istringstream iss(expr); stack<int> stk; string token; bool invalid = false; while (iss >> token && !invalid) { if (isOperator(token)) { // 检查栈中是否有足够操作数 if (stk.size() < 2) { invalid = true; break; } int b = stk.top(); stk.pop(); int a = stk.top(); stk.pop(); // 执行运算 if (token == "+") { stk.push(a + b); } else if (token == "-") { stk.push(a - b); } else if (token == "*") { stk.push(a * b); } else if (token == "/") { if (b == 0) { // 处理除零错误 invalid = true; break; } stk.push(a / b); // 整数除法 } } else if (isNumber(token)) { stk.push(stoi(token)); // 数字压栈 } else { invalid = true; // 非法字符 break; } } // 最终检查栈是否仅剩一个元素且无错误 if (invalid || stk.size() != 1) { cout << "INVALID" << endl; } else { cout << stk.top() << endl; } return 0; } ``` ### 关键点说明 1. **栈操作顺序**:运算符作用于栈顶两个元素时,**先弹出右操作数**(即第二个参数),例如 $a\ b\ -$ 实际计算 $a - b$。 2. **错误检测**: - **操作数不足**:运算符处理前检查栈大小。 - **除零错误**:除法运算前检查除数是否为0。 - **非法字符**:无法识别为数字或运算符时直接报错。 3. **输入处理**:使用 `istringstream` 分割字符串,避免手动处理空格。 此程序能有效处理合法输入并正确识别常见错误,如输入 `7 2 2 - /` 会因除零错误返回 `INVALID`,而 `7 -2 + 2 /` 会正确计算为 $2$。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值