这道题其实很简单 ,我早上九点看到题后,下午五点都还没解决,直到三天后,我才恍然大悟,把这道题做了出来。
为了让其它人可以不像我那么苦恼,所以在此我要把我解决的思路讲出来,让观众开心开心 。
首先说一下题目:
题目描述
有 n 列火车按 1 到 n 的顺序从东方左转进站,这个车站是南北方向的,它虽然无限长,只可惜是一个死胡同,而且站台只有一条股道,火车只能倒着从西方出去,而且每列火车必须进站,先进后出。
现在请你按字典序输出所有可能的出栈方案。
输入
输入一行一个整数 n。
输出
按字典序输出所有答案,每行一种,之间用空格隔开。w
就像这样,第一辆火车进站后,可以马上出站,也可以等第二辆火车进站后再出站,或者第三辆。 所以,假设有三辆火车,正确输入输出如下
输入:
3
输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 2 1
其中的3 1 2因为不符合规矩,被剔除了。
那么了解题目后,是时候说下我解决的思路了
(完整代码可以直接拉最后看噢,是c语言写的)
1辆火车有进站和出站两个动作,那么2辆火车就有4个动作,3辆6个…n辆就有n * 2个动作。
我只要用一个数组,记录下所有可能的动作,然后再用栈模拟这组记录,进行进站(压栈)和出站(弹栈),在弹栈的时候进行打印输出即可
那么难点主要有一个,如何记录下所有可能的动作,并剔除掉不符合现实的动作?
这里作者我就想到了用二叉树了,只要设定二叉树的左支是出站,右支是进站,那么一颗完整的二叉树即可表示完所有的动作,我们再剪掉不符合现实的分枝,就可以得到答案了(符合字典序的噢)。
比如这棵树,A、B、D路线肯定都是错的啦,不知道为什么的观众往下看…
(注意。。。我忘记给根结点画上+1了)
如我画的图(请忽略粗糙的画工,作者我也找不到好的工具唉)
比如我有2辆火车,那么一共会有4个动作,那么我的树就会有有4层,深度为4,每层代表一个动作(进、出站)。
-1代表出站,+1代表进站,我会用前序遍历,在每经过一个结点,我就用一个数组record记录下每个动作,比如名称为A的终端结点,到它那里,record={1,-1,-1,-1}。(注意,我默认根节点就为1了…)
很明显,只进1辆火车,却出了3辆火车,是不符合现实的,待会我再讲怎么在第3层就把它剪掉。
为什么我要用数组来记录呢,因为这样我就不用每次搜索都真的去压栈弹栈呀(滑稽),如果有数万条分支,每次都真的去动,会承受不住的!
下面我直接解释我的递归循环来前序遍历二叉树吧,不懂什么是前序遍历的翻一下我另一篇文章(即将会有,狗头),超简单的
void tree(SqStack* p, int* record, int fork, int deep) {
//首先,我们进来了一个节点,先让record记录下该层是左分支还是右分支
//应该-1还是+1(注意,其实-1、+1是我个人爱好,你们-9、+9都可以噢)
//fork为-1是左,1是右,左出右进
//比如我刚进来根节点,那么record[0] = 1,然后deep再自加1
record[deep++] = fork;
//该句是判断该支路是否符合火车进站的规矩
//judge函数其实是数一下之前的进站次数和出站次数,然后返回0或1
//不符合直接return,剪枝,可以省很多时间和内存噢
if (!judge(record, deep, p->length)) return ;
//如果深度到达限制,将停止继续探索
//并开始用“真的栈”按照record的记录打印
//这就是到达终端结点的标志,一切递归终结的地方
if(deep == p->length * 2) {
int val = 1;//用于进栈时赋值
for (int i = 1; i <= p->length * 2; i++) {
if (record[i-1] == 1) {
push(p, val++); //压栈,出来后val自加1
} else if (record[i-1] == -1) {
pop(p); //弹栈,我写的pop里面有输出函数了
if (i == p->length * 2) printf("\n");
else printf(" ");//为了符合输出格式而添加的
}
}
return ; //结束,返回上一层
}
//经过上面的重重判断后,发现没违反现实,也没到达终端,终于要生孩子了
tree(p, record, -1, deep); //进入左分支
//从左边出来后,继续探索右边噢
tree(p, record, 1, deep); //进入右分支
//都探索完后,返回上一层吧
return ;
}
上面的代码就好像下图一样,按着箭头探索
经历探索后,成功到达终端结点的,就是record{1, -1, 1, -1}
按照record记录,用栈去跟着压栈弹栈后,将会输出 2 1,这就是我们想要的结果,3个数如此,4个数也如此,那么n个数也同样可以搞定。
肯定会有人问我judge函数又是怎么回事,我也就懒得费口水了,看下面吧,反正judge函数内容很简单的。
下面代码我把压栈弹栈的push和pop放man函数后面,方便大家观看主要内容,如果观众是用c++或其它语言写,不用学我那样造轮子了(哭)
完整代码如下,直接复制粘贴就可以用噢↓
别看很长,因为我注释占一半,c语言的轮子也占一半,如果c++,我十几行就搞定的!(假的)
#include <stdio.h>
#include <stdlib.h>
typedef int sType;
typedef struct {
sType *data;
int length;
int top;
} SqStack;
SqStack* init(int );
void push(SqStack *, int);
void pop(SqStack *);
int judge(int *, int ,int );
void tree(SqStack *, int *, int , int );
//judge传入的三个参数分别是
//用于记录的record指针,当前层的深度,以及最大允许的深度
//通过record的记录,判断压栈和弹栈的次数有没有超过规定,从而返回0或1
int judge(int* record, int deep_now, int num_max) {
int num_push = 0; //压栈的次数
int num_pop = 0; //弹栈的次数
for (int i = 0; i < deep_now; i++) {
if (record[i] == 1) ++num_push;
else ++num_pop;
}
if (num_push > num_max || num_pop > num_max
|| num_push < num_pop) return 0; //不符合,返回0
return 1; //符合,返回1
}
//tree的三个参数分别是栈,记录record
//值为-1或1的fork,以及深度deepo
void tree(SqStack* p, int* record, int fork, int deep) {
//进来先记录下该层是左分支还是右分支
//fork为-1是左,1是右,左出右进
record[deep++] = fork;
//判断该支路是否符合火车进站的规矩
//不符合直接剪枝,可以省很多时间和内存
if (!judge(record, deep, p->length)) return ;
//如果深度到达限制,将停止继续探索,并开始用真的栈按照record的记录打印
if(deep == p->length * 2) {
int val = 1;//用于进栈时赋值
for (int i = 1; i <= p->length * 2; i++) {
if (record[i-1] == 1) {
push(p, val++); //压栈,出来后val自加1
} else if (record[i-1] == -1) {
pop(p); //弹栈,我写的pop里面有输出函数了
if (i == p->length * 2) printf("\n");
else printf(" ");//为了符合输出格式而添加的
}
}
return ; //结束,返回上一层
}
tree(p, record, -1, deep); //进入左分支
tree(p, record, 1, deep); //进入右分支
return ;
}
int main() {
int n;
scanf("%d", &n);
SqStack* p = init(n); //创建一个空栈,用来实操
//用于作模拟记录,这样可以在搜索时不用真的去压栈弹栈
//record占的大小是要两倍长的,因为压栈弹栈是两个动作
//总共有n个数,那么就会有n*2个动作
int* record = (int *)malloc(sizeof(int) * n * 2);
//进入递归搜索
//第三个参数传1进去,是让record[0]=1,即根结点为1
tree(p, record, 1, 0);
return 0;
}
//下面的内容可看可不看,主要是我c语言写的,要用来建栈,压栈和弹栈用的。
//建立一个空栈,返回值为指向该栈的指针
SqStack* init(int n) {
SqStack* p = (SqStack *)malloc(sizeof(SqStack));
p->data = (sType *)malloc(sizeof(sType) * n);
p->length = n;
p->top = -1;
return p;
}
//进栈
void push(SqStack *p ,int val) {
if (p->top == p->length) {
printf("push error");
return ;
}
p->data[++p->top] = val;
return ;
}
//出栈
void pop(SqStack *p) {
if (p->top == -1) {
printf("pop error");
return ;
}
printf("%d", p->data[p->top]);
p->top--;
return ;
}
好吧,写完后我才发现我写成这样有人能看的懂才怪…
请原谅是新手的我写的新手文吧…