我的代码:
#include <iostream>//用于输入输出流
#include <vector>//用于存储动态数组
#include <queue>//用于队列数据结构(FIFO)
#include <stack>//用于栈数据结构(LIFO)
#include <algorithm>//提供排序等算法的功能
using namespace std;//表示使用标准库中的所有组件
vector<string> result;//result 用于存储每次递归调用时生成的栈操作序列
// 写入结果
/*这个函数接受一个队列 q 作为输入,
将队列中的元素依次转换为字符串,
并添加到 result 向量中
最后将完整的栈操作序列(一个字符串)保存下来*/
void write_result(queue<int> q) {
string str;
//声明了一个空的字符串 str,用来存储队列元素转换为字符串后的结果
while (!q.empty()) {
//使用 while 循环遍历队列中的元素,直到队列为空
//q.empty() 检查队列是否为空,如果为空,循环停止
str += to_string(q.front());
//q.front() 获取队列的第一个元素
//to_string(q.front()) 将队列中的整数元素转换为字符串
//str += 将转换后的字符串添加到 str 的末尾
str += ' ';
//每个元素转换为字符串后,会在后面加一个空格,方便后续拼接其他元素
q.pop();
//q.pop() 移除队列中的第一个元素,以便接着处理下一个元素
//队列是先进先出(FIFO)的,因此 pop() 会把第一个入队的元素移除
}
result.push_back(str);
//将完成拼接后的字符串 str 添加到全局变量 result 的末尾
//result 是一个 vector<string>,用来存储所有拼接结果
}
// 递归求解
void solve(queue<int> in, stack<int> t, queue<int> out, bool flag) {
//in:输入队列,存放待处理的数据
//t:栈,用于模拟栈的入栈和出栈操作
//out:输出队列,存放最终的结果
//flag:标记,决定当前操作是入栈还是出栈
if (in.empty()) {
//如果 in 队列为空,表示所有数据已经入栈
if (flag)
return;
//此时如果 flag 为 true,表示不允许再执行入栈操作,直接返回
//如果栈 t 还有数据,弹出栈顶元素并将其加入 out 队列,直到栈为空
while (!t.empty()) {
out.push(t.top());
t.pop();
}
write_result(out);
//调用 write_result(out) 来记录结果
return;
} else {
if (flag) {
//如果 flag 为 true,表示进行入栈操作
t.push(in.front());
//将 in 队列的第一个元素 in.front() 入栈
in.pop();
} else {
//如果 flag 为 false,表示进行出栈操作
//只有栈 t 非空时才能进行出栈,否则直接返回
if (t.empty())
return;
out.push(t.top());
t.pop();
}
// 递归
solve(in, t, out, false);
//第一个递归调用 solve(in, t, out, false) 负责出栈操作
solve(in, t, out, true);
//第二个递归调用 solve(in, t, out, true) 负责入栈操作
}
}
int main() {
int num;
while (cin >> num) {
//输入一个整数 num,表示待处理的数字的数量
int sequence;
queue<int> in, out;
stack<int> t;
//输入一个包含 num 个整数的序列,将这个序列作为输入队列 in
for (int i = 0; i < num; ++i) {
cin >> sequence;
in.push(sequence);
}
solve(in, t, out, true);
/*调用 solve 函数来模拟栈操作
in 是输入队列,t 是栈,out 是输出队列,
true 表示开始时进行入栈操作*/
sort(result.begin(), result.end());
//对生成的栈操作序列(存储在 result 向量中)按字典顺序排序
//通过迭代器遍历 result 向量,并逐个输出每个栈操作序列
for (auto it = result.begin(); it != result.end(); ++it) {
cout << *it << endl;
}
result.clear();
//result.clear();:清空 result 向量,以便下一次循环使用
}
return 0;
}
这段代码的功能是模拟栈操作,并通过递归方法生成栈的操作序列;总结思路如下:
主要功能:
-
输入处理:
程序从标准输入获取一组整数,存储到队列in
中 -
递归求解栈操作:
- 使用递归的方式,通过入栈和出栈的操作来模拟栈的转换
- 每个递归过程选择是进行入栈操作(将队列中的元素压入栈)还是进行出栈操作(从栈中弹出元素并加入输出队列)
- 递归过程中,
flag
用来标记是执行入栈还是出栈操作
-
记录结果:
- 当队列
in
为空时,说明所有元素已处理完,程序会将栈中的元素依次弹出并存入out
队列中,记录栈的操作结果 - 结果会保存在
result
向量中,格式为字符串形式,记录了每次操作的队列状态
- 当队列
-
排序和输出:
- 对生成的所有栈操作序列进行字典顺序排序
- 最后将所有排序后的结果输出到标准输出
代码分析:
-
write_result(queue<int> q)
:将队列中的元素转换为字符串并存入result
向量;队列的元素被依次读取并转换为字符串,然后连接成一个大的字符串,最后将其保存到result
-
solve(queue<int> in, stack<int> t, queue<int> out, bool flag)
:核心递归函数,控制栈的入栈和出栈操作。递归地进行两种操作:- 入栈(当
flag
为true
时,执行入栈操作) - 出栈(当
flag
为false
时,执行出栈操作)
- 入栈(当
-
main()
:处理输入并调用递归函数solve;
对结果进行排序后输出,并清空result
以便下一次计算
具体操作:
- 入栈操作: 每次将队列中的一个元素压入栈
t
中,递归处理 - 出栈操作: 每次从栈中弹出元素并添加到输出队列
out
中,递归处理 - 递归终止的条件是当队列为空且栈中的元素已经处理完
总结:
- 程序通过递归的方式模拟了栈的操作,尝试所有可能的栈操作序列,并记录每次的队列状态
- 生成的所有栈操作序列按字典顺序排序后输出
flag
起到了控制递归过程中入栈和出栈操作的作用,递归地生成所有可能的栈操作序列,并最终输出结果
假设输入的数字序列是:1 2 3
,即我们有三个数字需要处理;我们将依次执行入栈和出栈操作,来模拟这些数字通过栈的过程
初始状态:
- 输入队列
in = [1, 2, 3]
- 输出队列
out = []
- 栈
t = []
- 递归标记
flag = true
,表示从队列in
中入栈
递归过程:
-
第一次递归(
flag = true
):- 从输入队列
in
中取出元素1
,并入栈 - 当前状态:
in = [2, 3]
t = [1]
out = []
- 递归调用
solve(in, t, out, false)
进行出栈操作
- 从输入队列
-
第二次递归(
flag = false
):- 栈非空,从栈中弹出
1
,并加入输出队列out
- 当前状态:
in = [2, 3]
t = []
out = [1]
- 递归调用
solve(in, t, out, true)
进行入栈操作
- 栈非空,从栈中弹出
-
第三次递归(
flag = true
):- 从输入队列
in
中取出元素2
,并入栈 - 当前状态:
in = [3]
t = [2]
out = [1]
- 递归调用
solve(in, t, out, false)
进行出栈操作
- 从输入队列
-
第四次递归(
flag = false
):- 栈非空,从栈中弹出
2
,并加入输出队列out
- 当前状态:
in = [3]
t = []
out = [1, 2]
- 递归调用
solve(in, t, out, true)
进行入栈操作
- 栈非空,从栈中弹出
-
第五次递归(
flag = true
):- 从输入队列
in
中取出元素3
,并入栈 - 当前状态:
in = []
t = [3]
out = [1, 2]
- 递归调用
solve(in, t, out, false)
进行出栈操作
- 从输入队列
-
第六次递归(
flag = false
):- 栈非空,从栈中弹出
3
,并加入输出队列out
- 当前状态:
in = []
t = []
out = [1, 2, 3]
- 递归返回,处理完所有元素
- 栈非空,从栈中弹出
结果记录:
- 当前的栈操作序列是
1 2 3
,我们将其保存到result
向量中
进一步递归:
递归的过程是一个深度优先搜索,搜索树的每个节点表示一次操作;在一个节点上,可能有两种操作:
- 入栈操作:如果输入队列不为空,则可以进行入栈操作
- 出栈操作:如果栈不为空,则可以进行出栈操作
举个例子
假设输入队列是 [1, 2, 3]
,我们将递归调用分为两个主要分支:入栈 和 出栈
-
第一层递归:
in = [1, 2, 3]
,t = []
,out = []
,我们选择入栈 1,然后进入下一层递归-
第二层递归:
in = [2, 3]
,t = [1]
,out = []
,我们选择入栈 2,然后进入下一层递归-
第三层递归:
in = [3]
,t = [1, 2]
,out = []
,我们选择入栈 3,然后进入下一层递归-
第四层递归:
in = []
,t = [1, 2, 3]
,out = []
,此时输入队列为空,可以开始出栈操作- 出栈操作:从栈中依次弹出元素,形成一个有效的栈操作序列
[1, 2, 3]
- 出栈操作:从栈中依次弹出元素,形成一个有效的栈操作序列
-
由于已经处理完所有元素,递归回退到上一层。
-
-
在第三层递归中,我们还可以选择出栈,首先出栈 2,再回到上一层进行其他操作
-
继续递归,直到所有可能的栈操作序列都被遍历
-
-
为什么递归会产生不同的栈操作序列?
在递归的过程中,入栈和出栈的顺序决定了栈操作序列的最终结果;例如,某些递归分支中,我们可能先入栈某个元素,而在其他分支中可能先出栈已入栈的元素;这种顺序的不同,会导致不同的栈操作序列
举个例子来说明
假设我们现在输入队列为 [1, 2, 3]
,递归过程中可能会产生如下几种不同的栈操作序列:
- 序列 1:
1 2 3
(依次入栈,再依次出栈) - 序列 2:
1 3 2
(先出栈 1,再入栈 2,再出栈 2,最后出栈 3) - 序列 3:
2 1 3
(先入栈 2,然后再出栈 1,再出栈 3) - 序列 4:
2 3 1
(先出栈 2,再出栈 3,最后出栈 1) - 序列 5:
3 1 2
(先入栈 3,然后再出栈 1,再出栈 2) - 序列 6:
3 2 1
(先入栈 3,接着入栈 2,再出栈 1,最后出栈 2)
排序和输出:
在所有递归操作完成后,所有的栈操作序列将被保存在 result
向量中;我们对这些序列进行字典顺序排序,最后输出排序后的结果
执行 sort(result.begin(), result.end())
时,排序的过程如下:
-
逐个比较字符串:
sort
会依次比较每两个字符串;例如,首先比较"1 2 3"
和"1 3 2"
:- 首先比较第一个字符
'1'
和'1'
,它们相同 - 接着比较第二个字符
' '
和' '
,它们也相同 - 然后比较字符
'2'
和'3'
,因为'2'
的 ASCII 值小于'3'
,所以"1 2 3"
会排在"1 3 2"
前面
- 首先比较第一个字符
-
继续比较其他字符串:接下来会比较所有其他字符串,直到整个向量中的元素都按字典顺序排好
-
最终结果:经过一系列的比较和交换,
result
中的元素最终按照字典顺序排列
最终输出:
假设所有栈操作序列为:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
这些就是所有可能的栈操作序列,经过字典排序后输出。
总结:
通过递归,代码遍历了所有的栈操作可能性,模拟了如何从输入队列到输出队列。每次递归选择入栈或出栈,直到所有元素都被正确处理并记录下操作序列