栈、Rails(poj1363)与Catalan数

是一种线性的只能在一端操作的后进先出的数据结构


Rails(poj1363)

若1,2,3,4,...,n依次进栈(但不一定是依次连续进栈),然后再出栈,判断某个出栈序列的合法性。
思路:人工模拟进栈、出栈的过程。例如,1,2,3,4,5依次进栈,现有出栈序列3,4,2,1,5,问是否合法?
第一个出栈的是3,栈目前空,所以1,2,3进栈;
栈目前非空,为1,2,3,栈顶是3;
3出栈;
第二个出栈的是4,栈目前非空,为1,2,栈顶是2;
4进栈,栈目前非空,为1,2,4;
4出栈;
第三个出栈的是2,栈目前非空,为1,2,栈顶是2;
2出栈;
第四个出栈的是1;1出栈;
第五个出栈的是5,栈目前空,但还有未进栈的,所以5进栈;
栈目前非空,为5;
5出栈;
栈空。
元素遍历完毕(意味着 都进栈了),出栈序列遍历完毕(意味着 都出栈了),因此序列合法。

代码:
#include <iostream>
using namespace std;
#include <stack>
bool if_perm_ok(int *p, int size)
{
    stack<int> tmp;    //申请一个栈,已初始化为空栈
    int i=0, j=1;            //i用来标记序列元素p[i],j用来表示待进栈的元素1,2,3,4,5
    while(j <= size+1)    //待进栈元素还没有用完
    {
        if(tmp.empty())    //若栈为空
        {
            if(j == size + 1) break;    //一种情况是所有元素入栈后均出栈,为正常清空,此时j == size + 1,跳出while循环
            else tmp.push(j++);        //另一种情况是过程中的某一点栈暂时为空,此时需要push当前元素
        }
        else        //若栈非空,判断栈顶元素与序列中的当前指向元素                                        
        {
            if(tmp.top() == p[i]) {tmp.pop(); ++i;}   //栈顶元素与出栈序列中的当前元素相同,说明该它出栈了;并指向序列中的下一个元素
            else tmp.push(j++);        //若不同,说明该使下一个元素进栈;如果序列是非法的,这一步会导致j>size,从而跳出while循环
        }
    }
    if (i+1 == j) return true; 
    else return false;
}
这一算法机械地模拟了判断过程, 时间复杂度为O(n)。
另有一 规律,合法的出栈序列, 最大元素之后的元素必须单调递减排列。可以作为选择题的判断依据。
想象(脑补进出栈的动画过程):在任意时刻,栈中的元素不管是否连续,从栈底到栈顶,总是递增的。 当栈再无元素可入时,必定是最大的元素在栈顶,此后只能出栈,因此,此后的序列必定是递减的。注意,这是序列合法的 必要条件而非充分条件,举个反例:3,1,6,5,4,2。

Catalan数

出栈序列总数是问题规模n的函数f(n),则:
f(1) = 1;
f(2) = 2;
f(3) = 5; 123, 132, 213, 231, 321
f(4) = ?  分解成子问题递归求解
(1)()()()---f(0)*f(3)
()(1)()()---f(1)*f(2),且第一个括号里只能是2
()()(1)()---f(2)*f(1),且前两个括号里只能是2,3 。因为1在第三个出栈,之前2必然已经出过栈,3必然已经出过栈,所以前两空必然是2和3。
()()()(1)---f(3)*f(0)
所以,f(4) = f(0)*f(3) + f(1)*f(2) + f(2)*f(1) + f(3)*f(0)。
f(n) = (sigma(i=0)to(n-1))f(i)f(n-1-i),这是一个O(n^2)的算法,因为要递归计算f(n-1), f(n-2)...f(0),每计算一个f(i)都需要i步。
有O(1)的算法称为Catalan数,f(n) = C(2n, n) - C(2n, n-1) = C(2n, n) / (n+1)。可用“折线法”形象化地证明。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值