关于栈的应用拓展
栈是常用的一种数据结构,有n令元素在栈顶端一侧等待进栈,栈顶端另一侧是出栈序列。你已经知道栈的操作有两·种:push和pop,前者是将一个元素进栈,后者是将栈顶元素弹出。现在要使用这两种操作,由一个操作序列可以得到一系列的输出序列。请你编程求出对于给定的n,计算并输出由操作数序列1,2,…,n,经过一系列操作可能得到的输出序列总数。
输入
一个整数n(1<=n<=15(50%), 1<=n<=1000(100%))
输出
一个整数,即可能输出序列的总数目。
此题最原始的方法是利用回溯,对栈操作的模拟,边界是
1.所有的元素全部出栈
2.所有的元素经过操作
#include <stdio.h>
/*
一个操作数序列,从1,2,一直到n,栈A的深度大于n。现在可以进行两种操作:
1.将一个数,从操作数序列的头端移动到栈的头端(对应数据结构栈的push操作)
2.将一个数,从栈的头端移动到输出序列的尾端(对应数据结构的pop操作)
现在对于任意一个N,输入端的数据一定是1,2,3...N,求出可能出现的输出端数据序列的种数。
解题思路:
就是利用递归回溯,每一个元素只可能进栈出栈一次,所以当所有元素都进过栈后统计所有情况的次数
*/
int sum;
/*
递归模拟入栈出栈的过程,因为问题是求解出栈的序列可能情况种类。
因此在进行递归操作时要:先出栈,再入栈
*/
void dfs(int top,int head,int n) //top 为栈顶位置,head为操作数序列(队列),经过一系列操作后队首位置
{
if(head==n+1) //所有元素都已经进栈操作过一次,该次入栈(出栈)操作结束
{
sum++;
return;
}
if(top>0)
{
dfs(top-1,head,n); /*未出过栈 ,则出栈*/
}
if(head<n+1)
{
dfs(top+1,head+1,n); /*未入过栈,则入栈*/
}
}
int main() //注意使用递归的方法去模拟出栈,入栈的操作,当数据稍大的时候,将会出现超时,或爆栈
{
int n;
scanf("%d",&n);
sum=0;
dfs(0,1,n); //开始时,栈为空,且操作数队列队首head指向序列第一个元素的位置
printf("%d\n",sum);
return 0;
}
回溯法理解简单,但是效率奇差,n在比较小的情况下能够胜任,但是n一旦达到比较大的数时,必定超时!那有没有效率更高的算法呢?
回溯法效率差的原因是记录了很多的重复运算,其实此题还可以用动归方法解决,f[i,j],i表示入栈的个数,j表示出栈的个数,那f[i,j]就表示入栈i个数中出j个数的,但是此题要注意的是出栈数不能大于入栈数,那动归方程该如何推导,再次谢谢一位某位具有探索精神的大神为我们做了细致的研究现在我把它的论文粘贴如下:
1 引 言
在实际应用和数据结构课程的教学中,栈作为一种基本结构非常重要[1][3][4][6]。已知给定序列,求出栈序列的数目、求所有出栈的序列、以及判断某个序列是否为合法的出栈序列[5][7],这类问题经常出现。在[3]中,对出栈序列的计数问题给出了介绍性的说明,由于结果的证明需要用到生成函数,[3]也只是直接给出了结论。本文提出使用“两点之间路径计数”的方法解决出栈序列的计数问题,在此基础上可以求所有出栈的序列,以及判断某个序列是否为合法的出栈序列。
2 问题分析
两点之间路径计数的问题
问题:假设A、B两点之间所有道路如图1中的方形网格线(5×5),规定从A到B只能够向右或向上移动,求A点到B点有几条路。