出栈序列与Catalan 0-1序列

设集合 N 中的元素按次序1,2,3,...,n 入栈,其所有不同的出栈序列的集合为Sn,则|Sn|=

1、结果证明:

  定义1、 集合 N={1,2,3,...,n}中的元素按一定次序入栈,其每个出栈序列是 N 的一个元排列,若 N 的两个元排列不同,则称为两个不同的出栈序列(栈序列),记栈序列集为 Sn

  定义2、 n个0和n个1组成2n位0-1序列,若对任意i(i=1,2,3,...,2n),序列前i项中1的个数不少于0的个数,则称此序列为Catalan 0-1序列,记Catanlan 0-1序列集为Tn

  定义3、 集合 N={1,2,3,...,n}的一个n元排列,若对任意i,j(j=1,2,3,...,n,且j>i),如j排在i之前,要么j与i相邻,要么j与i之间所排的任一数均大于i,则称该排列为N的栈排列,记栈排列集为Pn

  引理 Tn

  证明:由于在2n位上填入n个1,不填1的其余n位自动填0的序列个数是,从中减去不符合要求的序列数即为所求Tn。不符合要求的序列是指:从左到右扫描,出现0的个数超过1的个数的序列。

  不符合要求的序列在从左向右扫描时,必然在某一奇数2m+1位上首先出现m+1个0的累计数和m个1的累计数。此后的2(n-m)-1位上有n-m个1,n-m-1个0。如若把后面这部分2(n-m)-1位,0与1互换,使之成为n-m个0,n-m-1个1,那么便得到一个由n+1个0和n-1个1组成的2n位0-1序列。即一个不符合要求的序列对应于一个由n+1个0和n-1个1组成的一个排列。反之,任何一个由n+1个0和n-1个1组成的2n位0-1序列,由于0的个数多2个,2n是偶数,故在某一个奇数位上出现0的累计数超过1的累计数,同样在后面的部分,令0和1互换,使之成为一个由n个0和n个1组成的2n位0-1序列。也就是说,我们建立了不符合要求的序列n+1个0和n-1个1组成的2n位0-1序列之间的一一对应关系。

  而由n+1个0和n-1个1组成的2n位0-1序列共有

  因此,Tn

     定理1、集合 N={1,2,3,...,n}的n个元素按次序1,2,3,...,n入栈时,Sn

  证明:1):Sn到Tn存在单射:设集合N={1,2,3,...,n}中的元素按一定次序1,2,3,...,n入栈,对Sn中的任一出栈序列,可以用Tn中唯一一个Catalan 0-1序列描述其出栈过程。按照“入栈记1,出栈记0”的原理顺序可得一个长为2n的Catanlan 0-1序列,且具有唯一性。具体方法为:对任意i(i=1,2,3,...,n),设i在某个出栈序列中排在第j个位置(从左起)上,且在出栈序列的前j项中最大数为m,则在2n为0-1序列的第j+m个位置(从左起)上记0,这样,可以在n个位置上记0,其余位置均记1。如:1,2,3,4,5顺次入栈,出栈序列21453对应的0-1序列为1100110100。

  2):Sn到Tn存在满射。对Tn中每一个Catalan 0-1序列,从左到右,看成集合中的元素按1,2,3,...,n入栈时,入栈出栈的过程记录。按照“1看成入栈操作,0看成出栈操作”可唯一写出Sn中一个出栈序列,具体方法为:把0-1序列从左第一个1起上方写1,2,3,...,n;然后把序列的每个0(从左到右)与其左边最近的1配对,相应的1上方的数依次构成一个出栈序列。如:1100110100,其配对为:,因此,对应的唯一的出栈序列为2,1,3,4,5。故Sn到Tn存在一个双射。即:Tn=Sn

  由上面的分析,可以写出出栈序列与Catalan 0-1序列相互转换的程序,以下是我用写的C++代码:

#include <cstdlib>
#include <iostream>
#include <iterator>
#include <stack>
#include <vector>

using namespace std;

vector<int>  Map2Stack( vector<char>& list )...
{
      int   i, count=0, j=0;
      vector<int> out(list.size()/2);
      stack<int>  s;
      for( i=0; i<list.size(); ++i )
           if( list[i]=='1' )
               s.push( ++count );
           else
               out[j++]=s.top(), s.pop();
      return out;
}
     
      
vector<char>  Map2Catan( vector<int>& list )...
{
      int   i, Max=0;
      vector<int>   index(list.size()+1), Max_index(list.size()+1);
      vector<char>  catanlan(2*list.size(),'1');
      for( i=0; i<list.size(); ++i )...{
           index[list[i]]=i+1;//list[i]排在第i+1位
           if( list[i]>Max )
               Max=list[i];
           Max_index[i+1]=Max;//前i+1位的最大数为Max
      
}

      for( i=0; i<list.size(); ++i )
           catanlan[list[i]+Max_index[list[i]]-1]='0';
      return  catanlan;
}

int main(int argc, char *argv[])...
{
    int   list[]=...{2,1,4,5,3
}
;
    vector<int>  e(list,list+sizeof(list)/sizeof(list[0]));
    vector<char> r1=Map2Catan( e );
    copy( r1.begin(), r1.end(), ostream_iterator<char>(cout) );
    cout<<endl;
    vector<int>  r2=Map2Stack( r1 );
    copy( r2.begin(), r2.end(), ostream_iterator<int>(cout,"  ") );
    cout<<endl;
    system("PAUSE");
    return EXIT_SUCCESS;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
算法思路: 1. 建立一个辅助栈stack和一个输出序列out_seq; 2. 将输入序列依次压入辅助栈stack中; 3. 从1到n依次枚举每个数,假设当前枚举到的数为i,将其从辅助栈stack中弹出,并将其加入输出序列out_seq中; 4. 对于剩余的数字j(j>i),如果j还在辅助栈stack中,则必须要在i弹出后才能弹出,因为栈是先进后出的,所以只有当j之前的数字都已经弹出,j才能弹出; 5. 以此类推,直到所有数字都被弹出,此时输出序列out_seq即为一个可能的出栈序列。 算法实现: ``` #include <iostream> #include <stack> #include <vector> using namespace std; // 求解所有可能的出栈序列 void get_out_seq(int n, vector<vector<int>>& res) { stack<int> stack; vector<int> out_seq; stack.push(0); int i = 1; while (i <= n) { if (stack.top() == i - 1) { // 如果栈顶元素等于i-1,说明i-1已经弹出了 stack.pop(); if (stack.top() == n) { // 如果栈顶元素等于n,说明所有元素都已经弹出了 res.push_back(out_seq); } else { int j = stack.top() + 1; while (j <= n) { // 将所有还没弹出的数依次压入栈中 stack.push(j); j++; } } out_seq.pop_back(); // 回溯,将当前弹出的数从输出序列中删除 } else { // 如果栈顶元素不等于i-1,说明i-1还没有弹出,将i压入栈中 stack.push(i); out_seq.push_back(i); i++; } } } int main() { int n; cout << "请输入n:"; cin >> n; vector<vector<int>> res; get_out_seq(n, res); cout << "所有可能的出栈序列:" << endl; for (auto seq : res) { for (auto num : seq) { cout << num << " "; } cout << endl; } return 0; } ``` 算法分析: 时间复杂度:O(n×Cn),其中Cn表示Catalan数,即出栈序列的个数,Cn的计算公式为:Cn = (2n)! / ((n+1)!n!),因为对于每个数字都有两种选择:入栈或出栈,所以共有2^n种情况,但是其中有n个数字必须先入栈再出栈,所以实际上只有Cn种情况是合法的,因此时间复杂度为O(n×Cn)。 空间复杂度:O(n),其中n为输入序列的长度,需要使用一个辅助栈stack和一个输出序列out_seq,因此空间复杂度为O(n)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值