150.逆波兰表达式求值
题目叙述
根据逆波兰表示法(Reverse Polish Notation,RPN),求表达式的值。有效的算符包括 +
、-
、*
、/
。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意:
- 两个整数之间的除法只保留整数部分。
- 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入: tokens = ["2","1","+","3","*"]
输出: 9
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入: tokens = ["4","13","5","/","+"]
输出: 6
解释: 该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入: tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出: 22
解释:
该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
模式识别
本题适合使用栈来解决。逆波兰表达式的特点是运算符紧跟在操作数之后,当遇到运算符时,需要对之前的操作数进行相应运算。栈的后进先出(LIFO)特性与逆波兰表达式的计算逻辑相契合,在遇到运算符时,可从栈中取出最近的操作数进行计算。
考点分析
- 栈的应用:理解栈的基本操作(入栈、出栈),并运用栈来处理逆波兰表达式的计算。
- 字符串处理:将输入的字符串数组中的元素转换为数字或识别为运算符。
- 算术运算处理:实现不同运算符的算术运算,注意整数除法的截断规则。
所有解法
- 栈解法:使用栈来存储操作数,遇到运算符时从栈中取出操作数进行计算,再将结果压入栈中。这是解决逆波兰表达式求值的标准方法。
- 递归解法:可以将逆波兰表达式看作是一种递归结构,但实现相对复杂,且空间复杂度较高,一般不采用。
C 语言最优解法代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 计算逆波兰表达式的值
int evalRPN(char **tokens, int tokensSize) {
// 创建一个栈,大小为 tokensSize,因为最多会有 tokensSize 个操作数入栈
int *stack = (int *)malloc(tokensSize * sizeof(int));
// 栈顶指针,初始化为 -1 表示栈为空
int top = -1;
for (int i = 0; i < tokensSize; i++) {
// 如果当前元素是运算符
if (strcmp(tokens[i], "+") == 0 || strcmp(tokens[i], "-") == 0 ||
strcmp(tokens[i], "*") == 0 || strcmp(tokens[i], "/") == 0) {
// 从栈中取出两个操作数
int num2 = stack[top--]; // 第二个操作数
int num1 = stack[top--]; // 第一个操作数
// 根据不同的运算符进行相应的计算
if (strcmp(tokens[i], "+") == 0) {
stack[++top] = num1 + num2; // 加法运算
} else if (strcmp(tokens[i], "-") == 0) {
stack[++top] = num1 - num2; // 减法运算
} else if (strcmp(tokens[i], "*") == 0) {
stack[++top] = num1 * num2; // 乘法运算
} else if (strcmp(tokens[i], "/") == 0) {
stack[++top] = num1 / num2; // 除法运算
}
} else {
// 如果当前元素是操作数,将其转换为整数并压入栈中
stack[++top] = atoi(tokens[i]);
}
}
// 最终栈顶元素即为表达式的结果
int result = stack[top];
// 释放栈的内存
free(stack);
return result;
}
复杂度分析
- 时间复杂度:
O
(
n
)
O(n)
O(n),其中
n
n
n 是
tokens
数组的长度。需要遍历数组中的每个元素一次,每个元素的操作(入栈、出栈、计算)都是常数时间复杂度。 - 空间复杂度: O ( n ) O(n) O(n),主要是栈的空间开销,最坏情况下栈需要存储所有的操作数,即栈的最大深度为 n n n。