系列目录
88.合并两个有序数组
52.螺旋数组
567.字符串的排列
643.子数组最大平均数
150.逆波兰表达式
61.旋转链表
160.相交链表
83.删除排序链表中的重复元素
389.找不同
1491.去掉最低工资和最高工资后的工资平均值
896.单调序列
206.反转链表
92.反转链表II
141.环形链表
142.环型链表
150.逆波兰表达式
🌟逆波兰+栈
逆波兰表达式本质就是一种后缀表达式
class Solution {
public:
// 对 token 的任何修改都会传递给原始 string 对象上,因为传递的是引用而不是副本
bool isNumber(string& token)
{
// 若 函数参数既非又非+-*/ 则 返回true
return !(token == "+" || token == "-" || token == "*" || token == "/");
}
int evalRPN(vector<string>& tokens) {
stack<int> stk;
int n = tokens.size();
for (int i = 0; i < n; i ++) {
string& token = tokens[i];
if (isNumber(token)) {
// ASCII to Integer atoi()
// 整数栈,需要将操作符这种char类型转换成对应的int型才能压入栈
stk.push(atoi(token.c_str()));
} else {
// 先出栈的操作数应该放在操作符右边👉
int num2 = stk.top();
stk.pop();
int num1 = stk.top();
stk.pop();
// token数组(剩余)第1个元素(必定为操作符)
// 根据操作符进行相应的运算,并将结果推回栈中
switch (token[0]) {
case '+':
stk.push(num1 + num2);
break;
case '-':
stk.push(num1 - num2);
break;
case '*':
stk.push(num1 * num2);
break;
case '/':
stk.push(num1 / num2);
break;
}
}
} // for_end
return stk.top();
}
};
肢解剖析
string& token = tokens[i];
定义一个名为token
的引用,它引用了tokens
容器中索引为i
的string
对象
之后,可通过token
来访问和修改string
对象,而不需要使用tokens[i]
atoi()
"ASCII to Integer"
定义在<stdlib.h>
头文件中
实际上它并不是专门处理 ASCII 字符的,而是处理任何可表示为整数的字符串,即将字符串 (string )转换为整数 (integer )
stk.push(atoi(token.c_str()))
将 token
这个 string
类型的字符串转换为整数,并将这个整数推入 stk
这个整数栈中
这行代码执行了以下几个操作:
-
token.c_str():
token
是一个string
类型的引用c_str()
是string
类的一个成员函数,它返回一个指向正规 C 字符串的指针,内容与原string
相同 这个 C 字符串以'\0'
结尾 -
atoi(token.c_str()):
atoi
是一个标准库函数 (定义在<stdlib.h>
或<cstdlib>
中 ),用于将 C 字符串 (即char*
类型的字符串 )转换为int
类型的整数这里,它将
token.c_str()
返回的 C 字符串转换为整数 -
stk.push():
stk
是一个stack<int>
类型的对象,用于存储整数push
是stack
类的一个成员函数,用于在栈顶添加一个新元素这里,它将
atoi(token.c_str())
转换得到的整数推入栈中
这种转换和推(入)栈操作在逆波兰表达式求值算法中是很常见的
因为逆波兰表达式通常是由数字和操作符组成的字符串序列
需要将这些字符串转换为数字并存储在(整数)栈中以便后续计算
switch(开关)
语句:
switch
语句是一些编程语言都可适用的用于多路分支选择的结构
允许你基于一个表达式的值的不同情况来执行不同的代码块
switch
语句的基本语法如下:
switch (expression) {
case constant1:
// 当 expression 的值等于 constant1 时执行的代码
break;
case constant2:
// 当 expression 的值等于 constant2 时执行的代码
break;
// ... 可以有更多的 case
default:
// 当 expression 的值不匹配任何 case 时执行的代码
}
expression
是一个表达式,其值将被与case
语句中的各个常量进行比较case
关键字后面跟着一个常量表达式 (通常是整数或字符 ),如果expression
的值与该常量相等,则执行该case
下的代码块break
关键字用于终止switch
语句,防止代码自动执行到下一个case
default
是可选的,当expression
的值不匹配任何case
时,将执行default
下的代码块
switch (token[0]) {}
这里 token
是一个字符串或字符数组,token[0]
获取该字符串或字符数组的第一个字符
手动定义一个isNumber()
方法(函数)
- 判断一个字符串是否是操作数
- 如果
token
不是 这四个操作符中的任何一个(即是一个操作数),则返回true
,否则返回false
逆波兰表示法
逆波兰表示法(Reverse Polish Notation,RPN)是一种不需要括号来标明运算顺序的算术表示法
又称后缀表示法,波兰表示法即为前缀表达式
在逆波兰表示法中,操作符位于操作数之后,因此不需要括号来指明运算的优先级
在C++中,逆波兰表示法的一个常见应用是在实现计算器的表达式求值时
由于逆波兰表示法不需要括号,因此可以简化表达式的解析过程
示例:
#include <iostream>
#include <stack>
#include <string>
#include <sstream>
#include <cctype>
// 函数声明
double applyOp(double a, double b, char op);
int main() {
std::string expression = "2 3 + 4 *"; // 逆波兰表示法的表达式 (2 + 3) * 4
std::stack<double> values;
std::istringstream iss(expression);
std::string token;
while (iss >> token) {
if (std::isdigit(token[0])) { // 如果token是数字,则压入栈
double value = std::stod(token);
values.push(value);
} else { // 如果token是操作符,则从栈中弹出两个操作数进行计算
double val2 = values.top();
values.pop();
double val1 = values.top();
values.pop();
double result = applyOp(val1, val2, token[0]);
values.push(result);
}
}
// 栈中剩下的就是最终的结果
std::cout << "Result: " << values.top() << std::endl;
return 0;
}
double applyOp(double a, double b, char op) {
switch (op) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b != 0.0) return a / b;
else throw std::invalid_argument("Division by zero");
default:
throw std::invalid_argument("Invalid operator");
}
}
注解:
- 首先读取一个逆波兰表示法的字符串表达式,然后逐个处理每个token 如果token是一个数字,它将被转换为一个双精度浮点数并压入栈中
- 如果token是一个操作符,它将从栈中弹出两个操作数,应用操作符进行计算,并将结果压回栈中
- 栈中剩下的就是整个表达式的计算结果