【问题描述】对于一个栈,已知元素的进栈序列,判断一个由栈中所有元素组成的排列的出栈序列。
有N个数,则代表入栈序列为1,2,3,4,5...,N。求所有可能的出栈序列和总数。
代码如下
#include<iostream> #include<vector> #include<algorithm> #include<string> #include<stdio.h> using namespace std; int N = -1; int sum = 0;; //递归算法 //count:当前已经入栈的元素个数 //seq:当前栈 //result:出栈序列,从左到右为出栈序列 void printValidSequence(int count,vector<int> seq,vector<int> result){ if (count == N){ result.insert(result.end(), seq.rbegin(), seq.rend()); for (int i = 0; i < result.size(); ++i){ cout << result[i]; } cout << endl; sum++; return; } vector<int> tmp(seq.begin(), seq.end()); seq.push_back(++count); //下一个元素入栈 printValidSequence(count, seq, result); if (!tmp.empty()){ //当前元素出栈出栈 result.push_back(tmp.back()); tmp.pop_back(); printValidSequence(count-1, tmp, result); } } //非递归算法 //递归算法 //count:当前已经入栈的元素个数 //seq:当前栈 //result:出栈序列,从左到右为出栈序列 class node{ public: vector<int> seq; vector<int> result; int count; node(){ seq.clear(); result.clear(); count = 0; } void print(){ result.insert(result.end(), seq.rbegin(), seq.rend()); for (int i = 0; i < result.size(); ++i){ cout << result[i]; } cout << endl; } void add(){ result.push_back(seq.back()); seq.pop_back(); } void push(){ seq.push_back(++count); } }; void printValidSequence(int N){ vector<node> STACK; STACK.push_back(node()); while (true){ if (STACK.empty()){ break; } node tmp = STACK.back(); //出栈 node tmp2 = tmp;//入栈(=是拷贝) STACK.pop_back(); if (tmp.count == N){ tmp.print(); sum++; } else{ if (!tmp.seq.empty()){ tmp.add(); STACK.push_back(tmp); } tmp2.push(); STACK.push_back(tmp2); } } } int main(){ cin >> N; vector<int> seq; vector<int> result; printValidSequence(0, seq, result); //递归算法 cout << "总数为" << sum << endl; sum = 0; printValidSequence(N); //非递归算法 cout << "总数为" << sum << endl; system("pause"); return 0; }
也可以用Catalan数求解。
h(n)=h(0)h(n-1)+h(1)h(n-2)+...+h(n-1)h(0) h(0)=h(1)=1 <=> h(n)=C(2n,n)*(1/(1+n)) (C是组合符号)
对于本道题:
首先,我们设h(n)=序列个数为n的出栈序列种数。我们假定,最后出栈的元素为k,显然,k取不同值时的情况是相互独立的,也就是求出每种k最后出栈的情况数后可用加法原则,由于k最后出栈,因此,在k入栈之前,比k小的值均出栈,此处情况有f(k-1)种,而之后比k大的值入栈,且都在k之前出栈,因此有f(n-k)种方式,由于比k小和比k大的值入栈出栈情况是相互独立的,此处可用乘法原则,f(n-k)*f(k-1)种,求和便是Catalan递归式。
因此种类数是h(n)=C(2n,n)*(1/(1+n))
本题和另外一道题也是等价的:
一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果他
从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?
我们使用深度优先搜索来完成这道题目;
#include<iostream> #include<vector> #include<algorithm> #include<string> #include<stdio.h> using namespace std; int N = -1; int sum = 0; class node{ public: int x; int y; int dir; //1,2分别代表右和上 node(){ x = 0; y = 0; dir = 0; } node(int _x,int _y,int _dir){ x = _x; y = _y; dir = _dir; } int sum(){ return x + y; } bool compare(){ if (x > y){ return true; } else{ return false; } } }; void searchDest(int N){ vector<node> STACK; STACK.push_back(node()); while (true){ if (STACK.empty()){ break; } else{ node topNode = STACK.back(); if (topNode.sum() == 2 * N){ //到达终点 sum++; vector<int> stack1;//模拟出入栈,得到输出序列,向右走为入栈,向上走为出栈 vector<int> result; for (int i = 1; i < STACK.size(); ++i){ if (STACK[i].y - STACK[i - 1].y>0){ result.push_back(stack1.back()); stack1.pop_back(); } else{ stack1.push_back(STACK[i].x); } } for (int i = 0; i < result.size(); ++i){ cout << result[i]; } cout << endl; STACK.pop_back(); } else{ if (topNode.dir == 0){ STACK.back().dir++; if (topNode.x < N){ STACK.push_back(node(topNode.x + 1, topNode.y, 0));;//向右走 } } else if (topNode.dir == 1){ STACK.back().dir++; if (topNode.compare()){ STACK.push_back(node(topNode.x, topNode.y + 1, 0)); //向上走 } } else if (topNode.dir == 2){ STACK.pop_back(); } } } } } int main(){ cin >> N; searchDest(N); cout << "总数为:" << sum << endl; system("pause"); return 0; }
还有一个问题是,给出一个序列判断该序列是否为合法的输出序列:
比如输入序列是:1,2,3,4,5
序列53421就是合法序列:
有如下的判定定理:
对于出栈序列中的每一个数字,在它后面的、比它小的所有数字,一定是按递减顺序排列的。<=> 该输出序列是合法的