组合数
✅ 1. 几道经典的“组合数”算法题
✅ 2. 解释组合数算法及其优化方法(含快速求法)
📚 组合数经典算法题推荐:
1. Leetcode 77. 组合(Combinations) - 中等
给定两个整数 n 和 k,返回从 1 到 n 中选出 k 个数的所有组合。
- 
🧠 考察:回溯 + 组合生成 
- 
class Solution { public: vector<vector<int>> res; vector<int> path; void dfs(int n, int k, int start) { if (path.size() == k) { res.push_back(path); return; } for (int i = start; i <= n; i++) { path.push_back(i); dfs(n, k, i + 1); path.pop_back(); } } vector<vector<int>> combine(int n, int k) { dfs(n,k,1); return res; } };
2. Leetcode 62. 不同路径(Unique Paths) - 中等
一个机器人只能向右或下,从左上角走到右下角,共有多少种不同的路径?
- 
答案是组合数:C(m+n-2, n-1) 
- 
🧠 考察:组合数推导 + 优化 
- 
class Solution { public: /* int MOD=INT_MAX; long long c[2010][2010]; void init_comb() { for (int i = 0; i < 2010; ++i) { c[i][0] = 1; for (int j = 1; j <= i; ++j) c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % MOD; } }*///预处理,这道题并不推荐.. int uniquePaths(int m, int n) { init_comb(); long long resl = 1; int a = m + n - 2, b = min(m - 1, n - 1); for (int i = 1; i <= b; i++) { resl = resl * (a - i + 1) / i; } return c[a][b]; } };
3. Leetcode 118. 杨辉三角 - 简单
给出前
n行的杨辉三角(帕斯卡三角形),即组合数的二维结构。
- 
🧠 考察:组合数递推公式:C(n, k) = C(n-1, k) + C(n-1, k-1) 
- 
class Solution { public: vector<vector<int>> generate(int numRows) { vector<vector<int>> triangle; for (int i = 0; i < numRows; i++) { vector<int> row(i + 1, 1); for (int j = 1; j < i; j++) { row[j] = triangle[i - 1][j - 1] + triangle[i - 1][j]; } triangle.push_back(row); } return triangle; } };
4. Leetcode 1641. 统计字典序元音字符串的数目 - 中等
给你一个整数 n,返回长度为 n、仅由元音字母组成、字典序非递减的字符串数量。
- 
实质是一个可重组合数问题:C(n+5−1, 5−1) = C(n+4, 4) 
- 
🧠 考察:“可重组合”公式:C(n+k−1, k) 
- 
从 5 个有序元素中选 n 个(可重复),且顺序不能下降,相当于有放回的组合 + 字典序约束 
- 
相当于是 在 5 类物品中选 n 个,允许重复,且按顺序排列 
- 
class Solution { public: int countVowelStrings(int n) { int res = 1; for (int i = 1; i <= 4; i++) { res = res * (n + i) / i; } return res; } };
5. Leetcode 60. 第 k 个排列 - 困难
给定 n 和 k,返回第 k 个字典序排列。用到了组合数的思想来“跳过”某些分支。
- 
🧠 考察:阶乘 + 剪枝跳转 
- 
class Solution { public: string getPermutation(int n, int k) { vector<int> nums; for (int i = 1; i <= n; i++) nums.push_back(i); vector<int> fact(n); fact[0] = 1; for (int i = 1; i < n; i++) { fact[i] = fact[i - 1] * i; //cout << fact[i] << endl; } // 求阶乘 k--; string res; for (int i = n - 1; i >= 0; i--) { int idx = k / fact[i]; res += to_string(nums[idx]); nums.erase(nums.begin() + idx); k %= fact[i]; } return res; } };
📘 什么是组合数?
组合数 C(n, k) 表示从 n 个元素中选出 k 个的方式数。
✅ 公式定义:
C(n, k) = n! / (k! * (n - k)!)
⚡ 如何快速计算组合数?
🔹 方法 1:暴力公式法(适合小数)
long long comb(int n, int k) {
    long long res = 1;
    for (int i = 1; i <= k; ++i) {
        res = res * (n - i + 1) / i;
    }
    return res;
}
- 优点:不需打表,计算快(但要注意除法顺序)
🔹 方法 2:打表预处理阶乘(适合多次查询)
const int N = 100005;
const int MOD = 1e9+7;
long long fac[N], inv[N];
// 快速幂
long long qpow(long long a, long long b) {
    long long res = 1;
    while(b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}
// 初始化
void init() {
    fac[0] = 1;
    for(int i = 1; i < N; ++i) fac[i] = fac[i-1] * i % MOD;
    inv[N-1] = qpow(fac[N-1], MOD - 2);
    for(int i = N - 2; i >= 0; --i)
        inv[i] = inv[i + 1] * (i + 1) % MOD;
}
// 查询组合数
long long C(int n, int k) {
    if (k < 0 || k > n) return 0;
    return fac[n] * inv[k] % MOD * inv[n - k] % MOD;
}
- 优点:快速求组合数 + 模数取余
- 用于大规模组合数运算场景(如竞赛、字符串问题等)
如果你希望练习组合数生成 + 回溯算法
再推荐一个:
6. Leetcode 39. 组合总和 - 中等
找出所有可行的加和组合(元素可重复使用)
- 
🧠 考察:DFS + 剪枝 + 回溯 
- 
class Solution { public: vector<vector<int>> res; vector<int> path; void dfs(vector<int>& candidates, int target, int start) { if (target == 0) { res.push_back(path); return; } for (int i = start; i < candidates.size(); ++i) { if (candidates[i] > target) continue; path.push_back(candidates[i]); dfs(candidates, target - candidates[i], i);//可以重复使用当前的数 path.pop_back(); } } vector<vector<int>> combinationSum(vector<int>& candidates, int target) { sort(candidates.begin(), candidates.end()); dfs(candidates, target, 0); return res; } };

 
                
             
         浙公网安备 33010602011771号
浙公网安备 33010602011771号