栈出栈序列问题的探究与思考(卡特兰数)

一、引入

初学数据结构与算法,学到栈的时候,总是会遇到这样一类问题,

设输入序列为1,2,3,则经过栈的作用后可以得到()中不同的输出序列。

接着就开始一直在想,谁入栈,谁出栈,数字少还好,但数字一多起来,我就开始出现遗漏和重复,所以我只想有没有一种方法,或是说一种公式,可以让我在计算诸如此类问题时,可以最大的保证正确率,我抱着这样的目的,作此题解,以保证自己可以反复复习。

二、朴素算法

栈作为一种先进后出的特殊线性表,我们只能在他的一段进行操作。但由于允许出栈入栈穿插进行,我们可以选择将输入元素压入栈中,也可以在任意时刻把栈顶元素弹出。对于一个给定的输入序列,在栈的作用下,会产生不同的输出队列。

样例演示

在这里插入图片描述
如图所示,1, 2, 3的序列一共有五种,想起来老废劲了。接下来让我们找找有没有什么好方法来计算这道题。

三、卡特兰数的介绍

卡特兰数是一种数学上的数列,由法国数学家卡特兰(Catalan)在1838年首次提出。它的计算方式是:C0 = 1,Cn+1 = (2(2n+1)/(n+2))*Cn,其中n>=0。这个数列在组合数学、计算机科学、统计学等领域有广泛的应用。

卡特兰数是一个非常有用的数学工具,能够用于解决许多问题,其中之一就是栈的问题。

在使用栈时,常常需要考虑栈的各种操作的顺序,以确保栈操作的正确性。而卡特兰数正好可以帮助我们计算各种可能的栈操作顺序的数量,从而简化了栈操作的分析和设计。

这里我们可以使用卡特兰数来计算栈的出栈问题:
在这里插入图片描述
经过上述推导我们得到计算公式:
f ( n ) = C 2 n n n + 1 f(n) = \frac{C^n_{2n}}{n + 1} f(n)=n+1C2nn

我们可以验证一下:
f ( 3 ) = C 6 3 3 + 1 = 5 f(3) = \frac{C^3_6}{3 + 1} = 5 f(3)=3+1C63=5

答案完全正确!!!!!

四、卡特兰数的实现

如果只是为了作对这道题,我们完全可以记住公式,但作为热爱计算机的热血青年来说,我们一定要学会这道题的基本实现。

1.递推实现卡特兰数

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 100;
int n;
int C[N];

void catalan() {
    C[0] = C[1] = 1;
    for (int i = 2; i <= n; i ++) {
        for (int j = 0; j < i; j ++)
            C[i] += C[j] * C[i - j - 1];
    }
}

int main()
{
    cin >> n;

    catalan();

    cout << C[n] << endl;

    return 0;
}

2.组合数法实现卡特兰数

#include<iostream>
#include<algorithm>
#include<cstring>
#include <vector>

using namespace std;

const int N = 5010;

int primes[N], sum[N], cnt;
bool st[N];

void get_primes(int n)
{
    for(int i = 2; i <= n; i ++)
    {
        if(!st[i]) primes[cnt ++] = i;
        for(int j = 0; i * primes[j] <= n; j ++)
        {
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) break;
        }
    }
}

int get(int n, int p)
{
    int res = 0;
    while (n)
    {
        res += n/p;
        n = n/p;
    }

    return res;
}

vector<int> mul(vector<int> &A, int b)  // C = A * b, A >= 0, b >= 0
{
    vector<int> C;

    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}

int main()
{
    int m;
    cin >> m;
    int a = 2 * m, b = m;

    get_primes(a);

    for(int i = 0; i < cnt; i ++)
    {
        int p = primes[i];
        sum[i] = get(a, p) - get(a - b, p) - get(b, p);
    }

    vector<int> res;

    res.push_back(1);

    for(int i = 0; i < cnt; i ++)
    {
        for(int j = 0; j < sum[i]; j ++)
        {
            res = mul(res, primes[i]);
        }
    }
    
    int c = 0;
    for (int i = res.size() - 1; i >= 0; i -- )
    {
        c = c * 10 + res[i];
    }
    
    cout << c/(m + 1) << endl;
    return 0;
}

五、结语

这是这一章的全部内容,喜欢的话可以点赞支持一下,我们一起努力!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pigwantofly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值