LC 77. Combinations 回溯 / 改进 求组合数C(n, k)

Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.

For example,
If n = 4 and k = 2, a solution is:

[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

第一眼就感觉非常眼熟,这题和Permutation系列非常相似,都可以用回溯的方法来做。之前求Permutation已经写过总结了,当时递归函数用引用传递传当前得到的排列cur,难点是必须考虑在下一次循环开始前怎么处理cur从而不影响之后的排列可能,当时在myPermutation调用完之后还需再次swap。这道题也一样,用同一个cur在结束后只需要将刚刚加入的 i 给取出来即可。代码:
void myCombine(vector<vector<int>>& res, vector<int>& cur, int pos, int k, int n) {
    if (k == 0) {
        res.push_back(cur);
        return;
    }
    for (int i = pos; i <= n; i++) {
        cur.push_back(i);
        myCombine(res, cur, i + 1, k - 1, n);
        cur.pop_back();
    }
}
vector<vector<int>> combine(int n, int k) {
    vector<vector<int>> res;
    vector<int> cur;
    myCombine(res, cur, 1, k, n);
    return res;
}

测试了一下,用值传递的方式大概只超过了20%,处于最差的一批,用引用传递就可以达到80%,可见效率提升还是很高的。

另外,如果觉得push_back和pop_back的成本太大的话,可以实现声明一个长度为 k 的数组,之后直接通过在指定下标位置赋值即可,代码

void myCombine(vector<vector<int>>& res, vector<int>& cur, int pos, int count, int k, int n) {
    if (k == 0) {
        res.push_back(cur);
        return;
    }
    for (int i = pos; i <= n; i++) {
        cur[count] = i;
        myCombine(res, cur, i + 1, count + 1, k - 1, n);
    }
}
vector<vector<int>> combine(int n, int k) {
    vector<vector<int>> res;
    vector<int> cur(k, 0);
    myCombine(res, cur, 1, 0, k, n);
    return res;
}
网上还有迭代的写法:
vector<vector<int>> combine(int n, int k) {
		vector<vector<int>> result;
		int i = 0;
		vector<int> p(k, 0);
		while (i >= 0) {
			p[i]++;
			if (p[i] > n) --i;
			else if (i == k - 1) result.push_back(p);
			else {
			    ++i;
			    p[i] = p[i - 1];
			}
		}
		return result;
	}

以C(4,2)为例,代码得到的 p 为:

Incremented element at index 0
[1, 0]
Moved index to the right, and copied the value from the left
[1, 1]
Incremented element at index 1
[1, 2]
Combination found!
Added [1, 2] as an answer!
Incremented element at index 1
[1, 3]
Combination found!
Added [1, 3] as an answer!
Incremented element at index 1
[1, 4]
Combination found!
Added [1, 4] as an answer!
Incremented element at index 1
[1, 5]
n exceeded at 1, moving index to the left
Incremented element at index 0
[2, 5]
Moved index to the right, and copied the value from the left
[2, 2]
Incremented element at index 1
[2, 3]
Combination found!
Added [2, 3] as an answer!
Incremented element at index 1
[2, 4]
Combination found!
Added [2, 4] as an answer!
Incremented element at index 1
[2, 5]
n exceeded at 1, moving index to the left
Incremented element at index 0
[3, 5]
Moved index to the right, and copied the value from the left
[3, 3]
Incremented element at index 1
[3, 4]
Combination found!
Added [3, 4] as an answer!
Incremented element at index 1
[3, 5]
n exceeded at 1, moving index to the left
Incremented element at index 0
[4, 5]
Moved index to the right, and copied the value from the left
[4, 4]
Incremented element at index 1
[4, 5]
n exceeded at 1, moving index to the left
Incremented element at index 0
[5, 5]
n exceeded at 0, moving index to the left
[[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
不得不说真的是太机智了,学不来学不来。。。
在C语言中,利用阶乘函数计算组合数(也称为“n choose k”,记作 C(n, k) 或者 nPr)通常不是直接通过递归或者循环来计算阶乘,因为这种做法对于大数值可能会导致溢出。更常见的方法是使用组合公式: C(n, k) = n! / [k!(n-k)!] 其中,`n!` 表示 n 的阶乘,即从 1 到 n 连续相乘。 这里是一个简单的C语言函数来计算组合数,使用了递归的方法避免直接算阶乘: ```c #include <stdio.h> // 定义阶乘函数 unsigned long long factorial(int n) { if (n == 0 || n == 1) return 1; else return n * factorial(n - 1); } // 计算组合数函数 unsigned int combination(int n, int k) { unsigned long long numerator = factorial(n); unsigned long long denominator = factorial(k) * factorial(n - k); // 检查除法是否会溢出 if (denominator != 0) { return numerator / denominator; } else { printf("Error: Division by zero in combination calculation.\n"); return 0; } } int main() { int n, k; printf("Enter n and k for combinations: "); scanf("%d %d", &n, &k); if (k > n) printf("Invalid input: k should be less than or equal to n.\n"); else { unsigned int result = combination(n, k); printf("C(%d, %d) = %llu\n", n, k, result); } return 0; } ``` 在这个程序中,用户输入两个整数 n 和 k,然后 `combination` 函数会计算它们的组合数并打印结果。注意,这种方法对于较大的 n 和 k 可能不太高效,因为它涉及到多次阶乘运算。实际应用中,可以考虑使用动态规划或者其他优化算法来提高效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值