数据结构:递归:组合数(Combination formula)
目录
推导组合数的递归公式
杨辉三角 = 组合数图谱
🌳递归调用树可视化
代码实现
我们来分析“递归中的 Combination formula 问题”,也就是常说的组合数计算问题
推导组合数的递归公式
什么是组合问题?
组合问题的本质是从 n 个元素中选择 k 个元素,不考虑顺序,有多少种选法。
Step 1: 从一个最简单的事实出发
我们从两个最基本的事实(第一性)出发:
从 0 个数中选 0 个,有 1 种选法:C(0,0)= 1
从 n 个数中选 0 个,或者选 n 个,只有 1 种方式:C(n,0) = C(n,n) = 1
这是我们的边界条件(Base Cases)。
Step 2:构建思维模型:是否包含第 n 个元素?
我们从一个关键思想出发:
从 n 个元素中选 k 个,可以分为两类情况:
-
不选第 n 个元素:从前 n-1 个元素中选 k 个,记作 C(n-1, k)
-
选第 n 个元素:剩下从前 n-1 个元素中选 k-1 个,记作 C(n-1, k-1)
所以,我们得到核心递归关系:
这就是组合数的递归本质 —— 不依赖任何公式推导的、基于逻辑分类讨论的解法。
Step 3: 示例验证:
比如:从 4 个元素中选 2 个:
C(4, 2) = C(3, 2) + C(3, 1)
-
C(3, 2) =从前3个中选2个(不选第4个)
-
C(3,1)= 从前3个中选1个(因为已经选了第4个)
最终两者加起来就是从4个中选2个的方法总数。
🔍这就是递归(recursion):
-
大问题 C(n,k)
-
分解为两个更小的问题 C(n−1,k) 和 C(n−1,k−1)
-
一直递归下去,最终会到达 base case:C(n,0) 或 C(n,n)
杨辉三角 = 组合数图谱
杨辉三角其实就是组合数的排列。三角形第 n 行的第 k 个数,就是: C(n,k)
Row n → k →11 11 2 11 3 3 11 4 6 4 1
---------------------------------杨辉三角(Pascal's Triangle)
对应数值(左对齐)如下:
n\k → 0 1 2 3 4
0 1
1 1 1
2 1 2 1
3 1 3 3 1
4 1 4 6 4 1
举例:
即:杨辉三角中每个数都是其左上和右上两个数之和。
🌳递归调用树可视化
我们现在来画出计算C(4,2)的递归调用树,它完美对应杨辉三角的构建过程:
C(4,2)/ \C(3,2) C(3,1)/ \ / \C(2,2) C(2,1) C(2,1) C(2,0)| | | |1 ? ? 1/ \ / \C(1,1) C(1,0) C(1,0) C(1,1)| | | |1 1 1 1
所有底层节点(如 C(2,0), C(2,2), C(1,0), C(1,1))为 base case(边界条件)。
通过不断向下分解为两个更小的问题,每个组合数都是上层两个的和。
组合公式与递归的几何意义
-
杨辉三角的每一行的数值,都是通过上面两数之和得到;
-
这就意味着,从 n−1 行中,取 k−1 和 k 的值,就能得到 n 行的第 k 个值。
你可以这样理解:
-
C(n, k) 就像在杨辉三角中往下走到第 n 行第 k 个位置;
-
这个点的值由它“上方的两个点”(即 C(n−1,k−1) 和 C(n−1,k))决定;
所以,组合公式其实是动态构建杨辉三角的路径图。
代码实现
✅ 处理边界条件(base case)
if (k == 0 || k == n)return 1;
思路说明:
-
从 n 个中选 0 个只有 1 种方式:什么都不选;
-
从 n 个中选全部(即 k = n)也只有 1 种方式;
这就是我们的递归终止条件,防止无限递归。
✅ 写递归公式
return combination(n - 1, k - 1) + combination(n - 1, k);
组合的核心递推思想是:
-
选 or 不选当前元素:
-
不选:从前 n−1 个中选 k 个;
-
选了当前:从前 n−1 个中选 k−1个。
-
这正是递归的两个分支。
✅ 完整函数如下
int combination(int n, int k) {if (k == 0 || k == n)return 1;return combination(n - 1, k - 1) + combination(n - 1, k);
}
完整代码
#include <iostream>
using namespace std;int combination(int n, int k) {if (k == 0 || k == n)return 1;return combination(n - 1, k - 1) + combination(n - 1, k);
}int main() {cout << combination(5, 2) << endl; // 输出 10return 0;
}