C语言学习笔记20260703-牛牛与后缀表达式(逆波兰表达式)
1. 题目概述
本题要求计算一个特定格式的后缀表达式的值。
- 输入格式 :字符串
s,数字以#结尾作为分隔符,运算符为+、-、*。 - 示例 :
12#3#+15#*中缀形式(12+3)*15结果225。 - 数据范围 :字符串长度可达 10610^6106,运算结果绝对值不超过 101810^{18}1018(需使用
long long)。
2. 核心知识点:栈的应用
后缀表达式(又称逆波兰表达式)是计算机科学中经典的**栈(Stack)**应用场景。其求值过程不需要考虑运算符优先级或括号,算法逻辑非常直接。
算法流程
从左到右扫描表达式字符串:
- 遇到数字:解析出完整的数值,将其压入栈中。
- 遇到运算符 :
- 从栈顶弹出两个元素。
- 注意顺序 :先弹出的是右操作数 ,后弹出的是左操作数。
- 执行运算,将结果压回栈中。
- 结束:扫描完成后,栈中剩下的唯一元素即为最终结果。
⚠️ 易错点 :减法和除法不满足交换律。若栈中元素为
[a, b](b在栈顶),遇到-时,应计算a - b,而非b - a。
3. 代码实现与解析
以下是基于 C 语言的数组模拟栈解法:
c
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 给定一个后缀表达式,返回它的结果
* @param str string字符串
* @return long长整型
*/
long long legalExp(char* str)
{
// 定义足够大的数组模拟栈,防止溢出
const int MAXN = 1000005;
long long stack[MAXN];
// top 指向栈顶元素的下一个位置(即当前栈内元素个数)
int top = 0;
int len = strlen(str);
long long num = 0; // 用于暂存正在拼接的数字
for(int i = 0; i < len; i++)
{
char c = str[i];
// 1. 处理数字字符
if(c >= '0' && c <= '9')
{
// 累加构建多位数 (例如 "12" -> 1*10 + 2)
num = num * 10 + (c - '0');
}
// 2. 处理数字结束标志 '#'
else if(c == '#')
{
// 将构建好的数字入栈
stack[top++] = num;
num = 0; // 重置数字缓存
}
// 3. 处理运算符
else if(c == '+' || c == '-' || c == '*')
{
// 取出右操作数(栈顶)
long long b = stack[--top];
// 取出左操作数(新栈顶)
long long a = stack[--top];
long long res;
switch(c)
{
case '+': res = a + b; break;
case '-': res = a - b; break; // 注意是 a - b
case '*': res = a * b; break;
default: res = 0;
}
// 将运算结果压回栈中
stack[top++] = res;
}
}
// 最终栈顶元素即为结果,top指向栈顶元素的下一个位置(即当前栈内元素个数)
return stack[top - 1];
}
关键细节分析
-
多位数的处理 :
题目中数字可能不止一位(如
12)。代码中使用变量num进行累加 (num = num * 10 + ...),直到遇到#才确认数字结束并入栈。这是处理流式数字输入的常用技巧。 -
栈的实现方式 :
这里使用了静态数组
stack[MAXN]配合整数下标top来模拟栈。相比于 STL 的std::stack,数组模拟在竞赛或高性能场景下通常更快,且能精确控制内存大小。 -
数据类型 :
题目提示结果可能很大,因此栈和操作数必须使用
long long类型,防止整数溢出。
4. 复杂度分析
- 时间复杂度 :O(N)O(N)O(N)。其中 NNN 为字符串长度。我们只需要遍历一次字符串,每个字符的处理(入栈、出栈、计算)都是 O(1)O(1)O(1) 的操作。
- 空间复杂度 :O(N)O(N)O(N)。最坏情况下(例如所有数字都在前面,运算符在最后),栈的大小可能接近 N/2N/2N/2。
5. 总结
本题是后缀表达式求值的模板题。掌握此题的关键在于理解**"遇数入栈,遇符计算"**的原则,并正确处理多位数的解析以及运算数的左右顺序。这种基于栈的计算模型也是许多编译器和计算器底层实现的基础。