77. Combinations(组合)
1. 题目描述
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
2. 递归(Recursion)
2.1 解题思路
递归对于这道题是最直接和最简单的方法,因为迭代思路真的不好想,所以我们先来看看递归的思路。我们以n = 4,k = 3为例:
n = 4, k = 3
[1,2,3]
[1,2,4]
[1,3,4]
[2,3,4]
我们发现从n = 1开始,组合后面比较大的数,直到组合大小 == k,然后从n = 2继续重复上述步骤,直到n;注意,这里相当于固定了前面的数,然后加入后面的数,所以递归的代码思路用一个for循环访问所有的数,外层的递归等于之前被固定的数i,然后继续访问下一个数i + 1,并且当层循环结束去除当层的i,比如:
当得到[1,2,3],我们来到idx = 3的递归层数中,下一步访问idx + 1 = 4,但是组合数已经等于k,所以加入答案,返回上一级递归idx = 3,之后我们要先去除3,然后才能继续下一次循环i = 4,从而得到[1,2,4]。
2.2 实例代码
class Solution {
vector<vector<int>> ans;
vector<int> temp;
void recursionMethod(int idx, int n, int k) {
if (temp.size() == k) { ans.push_back(temp); return; }
for (int i = idx; i <= n; i++) {
temp.push_back(i); recursionMethod(i + 1, n, k); temp.pop_back();
}
}
public:
vector<vector<int>> combine(int n, int k) {
recursionMethod(1, n, k);
return this->ans;
}
};
3. 迭代(Iteration)
3.1 解题思路
如果递归是从一个假想的数组中,一个个的把数取出来,那么迭代的思路就是从0开始生成组合。首先,我们还是从例子入手,来观察迭代思路的规律:
n = 4, k = 2
[1,2]
[1,3]
[1,4]
[2,3]
[2,4]
[3,4]
先观察前三组,也是数字最小的三组:
[1,2]
[1,3]
[1,4]
都是最后一位序号数字不断+1,接着观察一下后面两组:
[2,3]
[2,4]
从[1,4] -> [2,3],我们可以认为从4回到1,然后1 + 1 = 2,然后把4赋值2,再+1;这步结束后,继续末尾序号数字+1,最后一组:[3,4],也可以用同样的操作完成。所以我们能否可以这样思考:从序号(idx = k - 1)开始,该序号的数字不断+1,然后通过移动idx来完成组合的生成。通过上面的例子,发现先不断末尾数字+1,如果超过n,则idx - 1,而且当idx == length - 1则生成一个合法组合。那么只有一个问题了:从哪里开始生成?一般有两种想法,要么从0开始,要么从最小的组合开始。其实两者方法都可以,只是后者直接跳过从0到第一个最小组合的过程。代码上层面,两者不断+1的先后顺序有些许不同,从0开始是先+1;而从最小组合开始是后+1;
3.2 实例代码
3.2.1 从零开始生成
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> ans;
vector<int> temp(k, 0);
int idx = 0;
while (idx >= 0) {
temp[idx]++;
if (temp[idx] > n) idx--;
else if (idx == k - 1) ans.push_back(temp);
else { idx++; temp[idx] = temp[idx - 1]; }
}
return ans;
}
};
3.2.2 从最小组合开始
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> ans;
vector<int> temp;
for (int i = 1; i <= k; i++) temp.push_back(i); // 生成第一个最小组合
int idx = k - 1;
while (true) {
if (temp[idx] > n) idx--;
else if (idx == k - 1) ans.push_back(temp);
else { idx++; temp[idx] = temp[idx - 1]; }
if (idx >= 0) temp[idx]++; else break;
}
return ans;
}
};