Evaluate Reverse Polish Notation

本文介绍如何使用栈来解析并求值逆波兰表达式,包括正则表达式的使用和算法实现。

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

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


public class Solution {
    public int evalRPN(String[] tokens) {
        if (tokens == null || tokens.length == 0) {
        	return 0;
        }
        Stack<Integer> stack = new Stack<Integer>();
        for (String string : tokens) {
        	int res = 0;
        	if (string.equals("+") || string.equals("-") || string.equals("*") || string.equals("/")) {
        		int b = stack.pop();
        		int a = stack.pop();
        		if (string.equals("+")) {
        			res += (a + b);
        		} else if (string.equals("-")) {
        			res += (a - b);
        		} else if (string.equals("*")) {
        			res += (a * b);
        		} else {
        			res += (a / b);
        		}
        		stack.push(res);
        	} else {
        		stack.push(Integer.valueOf(string));
        	}
		}
        return stack.pop();
    }
}
 
<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、付费专栏及课程。

余额充值