出栈序列问题——卡特兰数

在这里插入图片描述
大家新年快乐啊!!!(^ _ ^)
最近在刷题时遇见了这个题
是一个关于出栈方案的简单递归问题
后来Deepseek了一下才知道该题的背景
故留存在此供自己以后查阅

以下是关于卡特兰数的相关内容:

什么是卡特兰数?

卡特兰数(Catalan Number) 是一系列在组合数学中经常出现的自然数。卡特兰数的第 n 项(记作c n表示许多组合问题的解的数量。卡特兰数的前几项为:

C 0 = 1 , C 1 = 1 , C 2 = 2 , C 3 = 5 , C 4 = 14 , C 5 = 42 , … C_0 = 1, \quad C_1 = 1, \quad C_2 = 2, \quad C_3 = 5, \quad C_4 = 14, \quad C_5 = 42, \quad \ldots C0=1,C1=1,C2=2,C3=5,C4=14,C5=42,

卡特兰数的通项公式为:

C n = 1 n + 1 ( 2 n n ) C_n = \frac{1}{n+1} \binom{2n}{n} Cn=n+11(n2n)

其中, ( 2 n n ) \binom{2n}{n} (n2n) 是组合数,表示从 (2n) 个元素中选取 (n) 个元素的组合数。


为什么出栈方案能解释成卡特兰数?

问题背景

给定一个操作数序列 (1, 2, \ldots, n),通过栈的 pushpop 操作,可以得到一系列的输出序列。我们需要计算所有可能的输出序列的数量。

为什么是卡特兰数?
  1. 栈的性质

    • 栈是后进先出(LIFO)的数据结构。
    • 每次 push 操作将一个元素压入栈,每次 pop 操作将栈顶元素弹出并加入输出序列。
  2. 卡特兰数的定义

    • 卡特兰数 (C_n) 表示 (n) 个元素的合法出栈序列的数量。
    • 这是因为栈的操作可以看作是一个合法的括号匹配问题,而卡特兰数恰好是合法括号序列的数量。
  3. 递推关系

    • 假设某个时刻栈中有 (k) 个元素,剩下的 (n-1-k) 个元素在操作数序列中。
    • 对于每个 (k),合法的出栈序列数为 (C_k \times C_{n-1-k})。
    • 因此,总的出栈序列数为:
      C n = ∑ k = 0 n − 1 C k × C n − 1 − k C_n = \sum_{k=0}^{n-1} C_k \times C_{n-1-k} Cn=k=0n1Ck×Cn1k
      这正是卡特兰数的递推公式。

卡特兰数与二叉树的关系

卡特兰数 (C_n) 还可以表示以下组合问题的解的数量:

  1. 合法的括号序列

    • (n) 对括号的合法匹配方式的数量为 (C_n)。
  2. 二叉树的结构

    • (n) 个节点的不同形态的二叉树的数量为 (C_n)。
    • 例如,3 个节点的二叉树有 5 种不同的形态。
  3. 平面划分

    • 用 (n) 条不相交的对角线将一个凸多边形划分为三角形的方式数为 (C_{n-2})。
  4. Dyck 路径

    • 从 ((0, 0)) 到 ((2n, 0)) 的路径,每次只能向上或向右移动,且路径不越过 (x) 轴的方式数为 (C_n)。

代码实现:卡特兰数与出栈序列

以下是 C++ 代码,计算卡特兰数并解释出栈序列的数量:

#include <iostream>
#include <vector>
using namespace std;

// 计算卡特兰数
long long catalanNumber(int n) {
    vector<long long> dp(n + 1, 0);
    dp[0] = 1; // 空序列有一种输出方式

    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < i; ++j) {
            dp[i] += dp[j] * dp[i - 1 - j];
        }
    }

    return dp[n];
}

int main() {
    int n;
    cout << "请输入 n: ";
    cin >> n; // 输入 n

    // 计算卡特兰数
    long long result = catalanNumber(n);

    // 输出结果
    cout << "由 " << n << " 个元素组成的操作数序列,通过栈操作可能得到的输出序列的总数为: " << result << endl;

    return 0;
}

代码说明

  1. catalanNumber 函数

    • 使用动态规划计算卡特兰数。
    • 初始化 dp[0] = 1,表示空序列有一种输出方式。
    • 通过双重循环填充 dp 数组,递推计算每个 dp[i]
  2. 主函数

    • 读取输入的 (n)。
    • 调用 catalanNumber 函数计算结果并输出。

示例运行

输入:
请输入 n: 3
输出:
由 3 个元素组成的操作数序列,通过栈操作可能得到的输出序列的总数为: 5

卡特兰数与二叉树的关系代码

以下是 C++ 代码,计算 (n) 个节点的二叉树的数量(即卡特兰数):

#include <iostream>
#include <vector>
using namespace std;

// 计算卡特兰数(二叉树的数量)
long long catalanNumber(int n) {
    vector<long long> dp(n + 1, 0);
    dp[0] = 1; // 空树有一种形态

    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < i; ++j) {
            dp[i] += dp[j] * dp[i - 1 - j];
        }
    }

    return dp[n];
}

int main() {
    int n;
    cout << "请输入 n: ";
    cin >> n; // 输入 n

    // 计算卡特兰数
    long long result = catalanNumber(n);

    // 输出结果
    cout << n << " 个节点的二叉树的数量为: " << result << endl;

    return 0;
}

总结

  • 卡特兰数 是一个非常重要的组合数学概念,广泛应用于栈操作、二叉树、括号匹配等问题。
  • 出栈序列的数量是卡特兰数的一个经典应用,因为栈的操作可以转化为卡特兰数的递推关系。
  • 卡特兰数与二叉树的关系体现在:(n) 个节点的二叉树的数量等于第 (n) 个卡特兰数。
  • 通过动态规划可以高效计算卡特兰数,代码实现简单且易于理解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值