C语言学习笔记20260706-栈的压入、弹出序列验证
1. 题目概述
给定两个整数序列,第一个序列表示栈的压入顺序 (pushed),第二个序列表示弹出顺序 (popped)。请判断第二个序列是否可能是该栈的合法弹出序列。假设压入栈的所有数字均不相等。
- 输入 :多组测试数据。每组包含一个整数 nnn(序列长度),随后两行分别为
pushed和popped序列。 - 输出 :如果
popped是合法的出栈序列,输出 "Yes",否则输出 "No"。
2. 核心算法:辅助栈模拟法
解决此类问题的标准方法是直接模拟栈的操作过程 。我们不需要穷举所有可能的出栈情况,只需按照给定的 pushed 顺序入栈,并尽可能地去匹配 popped 序列。
💡 贪心策略
当我们把元素压入栈后,如果发现栈顶元素恰好等于当前期望弹出的元素 (即 popped 序列中指针指向的元素),那么我们必须立即将其弹出。
- 为什么? 因为如果不现在弹出,后续再压入新元素,这个栈顶元素就会被压在下面,永远无法在下一个时刻被弹出了。
- 结论:只要栈顶匹配,就立刻弹出,直到不匹配或栈为空为止。
3. 代码逻辑详解
关键变量说明
| 变量 | 作用 |
|---|---|
pushed[] |
存储入栈序列 |
popped[] |
存储目标出栈序列 |
stk[] |
辅助栈,用于模拟真实的入栈出栈过程 |
top |
栈顶指针,指向栈顶元素的下一个位置 |
p |
出栈指针 ,指向 popped 数组中当前需要匹配的元素下标 |
模拟流程
c
// 遍历每一个入栈元素
for (int i = 0; i < n; i++)
{
// 1. 将 pushed[i] 压入辅助栈
stk[top++] = pushed[i];
// 2. 检查栈顶是否满足出栈条件
// 只要栈不为空,且栈顶元素 == 当前待出栈元素(popped[p])
while (top > 0 && stk[top - 1] == popped[p])
{
top--; // 执行出栈操作
p++; // 移动出栈序列的指针,准备匹配下一个
}
}
结果判定
循环结束后,检查辅助栈的状态:
- 若
top == 0(栈为空):说明pushed中的所有元素都成功按照popped的顺序被弹出了 →\rightarrow→ Yes。 - 若
top != 0(栈非空):说明还有元素卡在栈里无法按顺序弹出 →\rightarrow→ No。
4. 复杂度分析
- 时间复杂度 :O(N)O(N)O(N)。虽然代码中有双重循环(
for套while),但每个元素最多只会被压入栈一次、弹出栈一次。因此总操作次数是线性的。 - 空间复杂度 :O(N)O(N)O(N)。需要一个辅助栈
stk来存储中间状态,最坏情况下栈的大小为 NNN。
5. 完整代码实现
以下是基于你提供的代码整理的完整版本,增加了注释以便于理解:
c
#include <stdio.h>
int main()
{
int q;
if (scanf("%d", &q) != 1) return 0; // 读取测试组数
// 使用 static 数组避免大数组导致栈溢出,适配 1e5 规模
static int pushed[100010];
static int popped[100010];
static int stk[100010];
while (q--)
{
int n;
scanf("%d", &n);
// 读入入栈序列
for (int i = 0; i < n; i++)
scanf("%d", &pushed[i]);
// 读入出栈序列
for (int i = 0; i < n; i++)
scanf("%d", &popped[i]);
int top = 0; // 栈顶指针
int p = 0; // popped 数组的索引指针
// 开始模拟
for (int i = 0; i < n; i++)
{
// 1. 元素入栈
stk[top++] = pushed[i];
// 2. 贪心匹配:如果栈顶元素等于当前应该弹出的元素,则持续弹出
while (top > 0 && stk[top - 1] == popped[p])
{
top--; // 出栈
p++; // 指向下一个待弹出元素
}
}
// 3. 判断结果
if (top == 0)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}