当前位置: 首页 > news >正文

出栈次序问题 (转)

编号为 1 到 n 的 n 个元素,顺序的进入一个栈,则可能的出栈序列有多少种?

C_n = \frac{1}{n+1}{2n \choose n} = \frac{(2n)!}{(n+1)!n!}
 
有关堆栈和Catalan数的思考


*
* *
* * *
* * * *
* * * * * 
形如这样的直角三角形网格,从左上角开始,只能向右走和向下走,问总共有多少种走法?

问题的由来:编号为 1 到 n 的 n 个元素,顺序的进入一个栈,则可能的出栈序列有多少种? 

对问题的转化与思考:n 个元素进栈和出栈,总共要经历 n 次进栈和 n 次出栈。这就相当于对这 2n 步操作进行排列。

一 个模型:一个 n*n 的正方形网格,从左上角顶点到右下角顶点,只能向右走和向下走。问共有多少种走法。如果将向右走对应上述问题的进栈,向下走对应上述问题的出栈,那么,可 以视此模型为对上述问题的具体描述。而解决此问题,只要在总共从左上角到右下角的2n步中,选定向右走的步数,即共有C(n 2n)中走法。

但是存在一个问题,如果走法越过了对角线,那么对应到上述问题是出栈数比入栈数多,这是不符合实际的。

对以上模型进行处理,对角线将以上正方形网格分成两部分,只留下包含对角线在内的下半部分,那么就不会出现越过对角线的问题。而这问题就是开始提出的问题。
-------------------------------------------------------

问题等价于:n个1和n个0组成一2n位的2进制数,要求从左到右扫描,1的累计数不小于0的累计数,试求满足这条件的数有多少?
解答: 设P2n为这样所得的数的个数。在2n位上填入n个1的方案数为 C(n 2n)
不填1的其余n位自动填以数0。从C(n 2n)中减去不符合要求的方案数即为所求。
不合要求的数指的是从左而右扫描,出现0的累计数超过1的累计数的数。

不合要求的数的特征是从左而右扫描时,必然在某一奇数2m+1位上首先出现m+1个的累计数,和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,结果得 1个由n+1个0和n-1个1组成的2n位数,即一个不合要求的数对应于一个由n-1个0和n+1个1组成的一个排列。

反过来,任何一个 由n+1个0,n-1个1组成的2n位数,由于0的个数多2个,2n是偶数,故必在某一个奇数位上出现0的累计数超过1的累计数。同样在后面的部分,令0 和1互换,使之成为由n个0和n个1组成的2n位数。即n+1个0和n-1个1组成的2n位数,必对应于一个不合要求的数。

用上述方法建立了由n+1个0和n-1个1组成的2n位数,与由n个0和n个1组成的2n位数中从左向右扫描出现0的累计数超过1的累计数的数一一对应。

例如 10100101

是由4个0和4个1组成的8位2进制数。但从左而右扫描在第5位(显示为红色)出现0的累计数3超过1的累计数2,它对应于由3个1,5个0组成的10100010。

反过来 10100010

对应于 10100101

因而不合要求的2n位数与n+1个0,n-1个1组成的排列一一对应,故有

P2n = C(n 2n)— C(n+1 2n)

这个结果是一个“卡塔兰数”Catalan,在组合数学中有介绍,可以参阅有关资料。

-----------------------------------------------------

是否可以用一种更合乎思维习惯的方式解决这个问题呢
可以把这个问题描述为一个二元组,(n, 0) 表示有n个元素等待进栈, 0 个元素已进栈, 这相当于问题最初的状况. 接着问题转化为(n-1,1). 可以这么说(n,0) = (n-1,1). 而对于(n-1,1)则相当于(n-1,0)+(n-2,2).
(n-1,0)表示栈中的一个元素出栈, (n-2, 2)表示又有一个元素入栈.
把问题一般话,则(n,m)的排列问题可以转化为(n,m-1)+(n-1,m+1) 此时m>=1, 因为必须栈中有元素才可以出栈.当m=0则(n,0)的问题只能转化为(n-1,1). 当问题为(0, m)时得到递归边界,这个问题的解是只有一种排列.

程序如下:
#include <stdio.h>

#define ELEMNUM 6;


int getPermuStack(int n, int m)
{
if(n == 0)//递归边界
return 1;
if(m == 0)//(n,0)问题的处理
return getPermuStack(n-1, 1);
return getPermuStack(n, m-1) + getPermuStack(n-1, m+1);
}


int main()
{
printf("The total count of stackout permutation is %d.", getPermuStack(6, 0));
return 0;
}

 

 

一个长度为n的无重复序列入栈的所有出栈方式

——by hahabrother

这是一个很有趣的问题,例如123这三个数字,入栈并出栈共有5种方式,分别为:321312231213123。那么对于长度为n的无重复序列中所有的出栈方式有哪些呢?

为了设计计算的算法,我们可以用队列(queue)来模拟输入,队列的输出则按照原先序列的顺序。使用一个栈(stack)来模拟入栈和出栈,结果保存在另外一个队列(queue)中。

现在的问题来了,怎么样可以实现所有的出栈入栈操作。首先来看看出栈和入栈是怎么回事,对于123这个序列,1先入栈之后有两种选择,1出栈和2入栈,而若2已经入栈之后,在2出栈之前1则不能先行出栈,故对于1我们只需要考虑其在2入栈之前出栈的情况,若1在栈内时2入栈,则12只能看成一个整体。

这样就可以用递归的方式求解,伪代码如下:

dostack(输入队列,中间栈,输出队列)

if(输入队列为空)

    if(中间栈为空)

        输出输出队列中的结果

    else

中间栈出栈,放入输出队列

dostack(输入队列,中间栈,输出队列)

else

    if(中间栈非空)

        新建输入队列2、中间栈2、输出队列2

        中间栈2出栈并放入输出队列2

dostack(输入队列2,中间栈2,输出队列2)

    输入队列出队一个数并压入中间栈

dostack(输入队列,中间栈,输出队列)

其基本思想为对于中间栈的每一个时刻拍照,都递归其后续的所有可能,由于在递归返回的时候还需要递归前的信息,所以每次递归都是新建数据结构而保存当前时刻的状态。若输入队列已经为空,则中间栈只有一种出栈方式,中间栈也为空时递归结束。

详细代码如下:

输入为序列的长度n,初始化序列为1,2,3…n,而输出则为所有可能的出栈数列。

 

 
#include <iostream>
#include <stack>
#include <queue>
using namespace std; 
int n,i,j;
int res;
stack <int> s;
queue <int> in,out;
void clear(stack <int> &s)
{
     while(!s.empty())
     s.pop();
}
void clear(queue <int> &s)
{
    while(!s.empty())
    s.pop();
}
void print(queue <int> i)
{
    while(!i.empty())
    {
        cout<<i.front();
        i.pop();
    }
    cout<<endl;
}
void dostack(queue <int> in,stack <int> s,queue <int> out)
{
    if(in.empty())
    {
        if(s.empty())
        {
            res++;
            print(out);
        }
        else
        {
            out.push(s.top());
            s.pop();
            dostack(in,s,out);
        }
    }
    else
    {
        if(!s.empty())
        {
            stack <int> ts;
            queue <int> tin,tout;
            tin=in;
            ts=s;
            tout=out;
            tout.push(ts.top());
            ts.pop();
            dostack(tin,ts,tout);
        }
        s.push(in.front());
        in.pop();
        dostack(in,s,out);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    while(cin>>n)
    {
        res=0;
        clear(in);
        clear(out);
        clear(s);
        for(i=n;i>=1;i--)
            in.push(i);
        dostack(in,s,out);
        cout<<res<<endl;
    }
    return 0;
}

 

 

转载于:https://www.cnblogs.com/ivorfeng/p/3592821.html

相关文章:

  • 使用SSM+Solr优雅的实现电商项目中的搜索功能
  • JSON格式
  • 安卓安装APK报错error: INSTALL_FAILED_UPDATE_INCOMPATIBLE
  • Android TV 开发(3)
  • [Head First设计模式]策略模式
  • CPU 已不足以驱动屏幕指纹识别技术,于是 vivo 用 DSP 来加速
  • 定义/控制自定义属性的使用
  • 【产品经理入门记】产品经理需要掌握的十大知识模块
  • 七、我的人生经历《我践行诚信的过程》
  • 总是保存下拉选项的第一个值
  • 一个xss漏洞到内网漫游【送多年心血打造的大礼包啦!】
  • Python基础06 循环
  • winscp 怎么用私钥文件登录的,以.ppk结尾的密钥文件
  • 敏捷开发---综述
  • kafka扫盲笔记,实战入门
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • Centos6.8 使用rpm安装mysql5.7
  • git 常用命令
  • leetcode-27. Remove Element
  • MySQL的数据类型
  • Mysql数据库的条件查询语句
  • oschina
  • vuex 笔记整理
  • 机器学习 vs. 深度学习
  • 检测对象或数组
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 王永庆:技术创新改变教育未来
  • 线性表及其算法(java实现)
  • 异常机制详解
  • #define,static,const,三种常量的区别
  • #Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()
  • #stm32驱动外设模块总结w5500模块
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (笔试题)合法字符串
  • (动态规划)5. 最长回文子串 java解决
  • (剑指Offer)面试题34:丑数
  • (九)c52学习之旅-定时器
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (十六)一篇文章学会Java的常用API
  • (四)Tiki-taka算法(TTA)求解无人机三维路径规划研究(MATLAB)
  • (转)Linux下编译安装log4cxx
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • ***监测系统的构建(chkrootkit )
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .gitattributes 文件
  • .jks文件(JAVA KeyStore)
  • .net core 依赖注入的基本用发
  • .NET Core中的去虚
  • .Net 中的反射(动态创建类型实例) - Part.4(转自http://www.tracefact.net/CLR-and-Framework/Reflection-Part4.aspx)...
  • .NET 中什么样的类是可使用 await 异步等待的?
  • .NET应用架构设计:原则、模式与实践 目录预览
  • .pop ----remove 删除
  • /etc/shadow字段详解
  • [.net 面向对象程序设计进阶] (19) 异步(Asynchronous) 使用异步创建快速响应和可伸缩性的应用程序...