
大家新年快乐啊!!!(^ _ ^)
最近在刷题时遇见了这个题
是一个关于出栈方案的简单递归问题
后来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),通过栈的 push 和 pop 操作,可以得到一系列的输出序列。我们需要计算所有可能的输出序列的数量。
为什么是卡特兰数?
-
栈的性质:
- 栈是后进先出(LIFO)的数据结构。
- 每次 push 操作将一个元素压入栈,每次 pop 操作将栈顶元素弹出并加入输出序列。
-
卡特兰数的定义:
- 卡特兰数 (C_n) 表示 (n) 个元素的合法出栈序列的数量。
- 这是因为栈的操作可以看作是一个合法的括号匹配问题,而卡特兰数恰好是合法括号序列的数量。
-
递推关系:
- 假设某个时刻栈中有 (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=0∑n−1Ck×Cn−1−k
这正是卡特兰数的递推公式。
卡特兰数与二叉树的关系
卡特兰数 (C_n) 还可以表示以下组合问题的解的数量:
-
合法的括号序列:
- (n) 对括号的合法匹配方式的数量为 (C_n)。
-
二叉树的结构:
- (n) 个节点的不同形态的二叉树的数量为 (C_n)。
- 例如,3 个节点的二叉树有 5 种不同的形态。
-
平面划分:
- 用 (n) 条不相交的对角线将一个凸多边形划分为三角形的方式数为 (C_{n-2})。
-
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;
}
代码说明
-
catalanNumber函数:- 使用动态规划计算卡特兰数。
- 初始化
dp[0] = 1,表示空序列有一种输出方式。 - 通过双重循环填充
dp数组,递推计算每个dp[i]。
-
主函数:
- 读取输入的 (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) 个卡特兰数。
- 通过动态规划可以高效计算卡特兰数,代码实现简单且易于理解。


419

被折叠的 条评论
为什么被折叠?



