给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
示例 2:
输入:n = 1, k = 1
输出:[[1]]
(1)c++实现代码
class Solution {
private:
vector> result; // 存放符合条件结果的集合
int stack[999], top = -1; // 用来存放符合条件结果
void backtracking(int n, int k, int startIndex) {
// 当结果的长度符合时,将结果存放到result结果集合中
if (top+1 == k) {
vector ans;
for(int i=0;i<=top;i++){
ans.push_back(stack[i]);
}
result.push_back(ans);
return;
}
for (int i = startIndex; i <= n; i++) {
stack[++top] = i;
backtracking(n, k, i + 1); // 递归
top--;// 回溯,撤销处理的节点
}
}
public:
vector> combine(int n, int k) {
result.clear();
backtracking(n, k, 1);
return result;
}
};
(2)c语言实现代码
struct Combination {
// 嵌套数组结果
int **result;
// 中间数组
int *path;
// 中间数组的长度
int pathSize;
// 结果数组的总数
int returnSize;
};
void backtrack(struct Combination *com, int n, int k, int startIndex) {
// 当组合的大小达到k时,将其添加到结果中
if (com->pathSize == k) {
// 在result的数组returnSize(也就是第几个结果集的位置)的位置提前申请一个空间
com->result[com->returnSize] = (int *)malloc(k * sizeof(int));
// 将中间数组存入result数组中
for (int i = 0; i < k; i++) {
com->result[com->returnSize][i] = com->path[i];
}
// 将结果集+1
com->returnSize++;
return;
}
// 横向遍历(加入了剪枝操作,即最后组成的数无法到要求直接剪掉即可)
for (int i = startIndex; i <= n - (k - com->pathSize) + 1; i++) {
// 处理当前节点
com->path[com->pathSize] = i;
// 中间数组长度+1
com->pathSize++;
// 纵向遍历(选择之后不能继续再选,即i+1)
backtrack(com, n, k, i + 1);
// 回溯,撤销处理的节点
com->pathSize--;
}
}
int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {
// 定义暂存结构体
struct Combination com;
com.returnSize = 0;
com.pathSize = 0;
// 初始条件是否符号条件
if (n <= 0 || k <= 0 || k > n) {
return NULL;
}
// 计算组合的总数
int totalCombinations = 1;
for (int i = 1; i <= k; i++) {
totalCombinations *= (n - i + 1);
totalCombinations /= i;
}
// 分配结果数组的空间
com.result = (int **)malloc(totalCombinations * sizeof(int *));
com.path = (int *)malloc(k * sizeof(int));
// 使用回溯法生成组合
backtrack(&com, n, k, 1);
// 返回每一个子数组的大小
*returnColumnSizes = (int *)malloc(com.returnSize * sizeof(int));
for (int i = 0; i < com.returnSize; i++) {
(*returnColumnSizes)[i] = k;
}
// 返回结果数组的大小
*returnSize = com.returnSize;
return com.result;
}
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1]
输出:[[1]]
c++代码实现
class Solution {
private:
vector> result;
int stack[999], top=-1;
void backtracking (vector& nums, vector& used) {
// 此时说明找到了一组
if (top+1 == nums.size()) {
vector ans;
for(int i=0;i<=top;i++){
ans.push_back(stack[i]);
}
result.push_back(ans);
return;
}
for (int i = 0; i < nums.size(); i++) {
// 已经使用过直接跳过即可
if(used[i] == true){
continue;
}
// used[i - 1] == true,说明同一树枝nums[i - 1]使用过
// used[i - 1] == false,说明同一树层nums[i - 1]使用过
// 如果同一树层nums[i - 1]使用过则直接跳过
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
continue;
}
used[i] = true;
stack[++top] = nums[i];
backtracking(nums, used);
top--;
used[i] = false;
}
}
public:
vector> permute(vector& nums) {
result.clear();
sort(nums.begin(), nums.end()); // 排序
vector used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
c语言实现
struct Combination{
// 结果数组
int **result;
int resultSize;
// 中间数组
int *path;
int pathSize;
};
void backtrace(struct Combination *com, int nums[], int n, int used[])
{
// 当组合的大小达到k时,将其添加到结果中
if(com->pathSize == n){
com->result[com->resultSize] = (int*)malloc(n*sizeof(int));
for(int i=0;iresult[com->resultSize][i] = com->path[i];
}
com->resultSize++;
return;
}
// 横向遍历
for(int i=0; ipath[com->pathSize] = nums[i];
com->pathSize++;
// 纵向遍历
backtrace(com, nums, n, used);
com->pathSize--;
used[i] = 0;
}
}
int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
struct Combination com;
com.resultSize = 0;
com.pathSize = 0;
// 结果集总数
int total = 1;
for(int i=1;i<=numsSize;i++){
total *= i;
}
// malloc申请空间
com.result = (int**)malloc(total*sizeof(int*)); // 嵌套数组
com.path = (int*)malloc(numsSize*sizeof(int)); // 数组
// 状态数组
int used[numsSize];
for(int i=0;i
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
c++代码实现
class Solution {
private:
vector> result;
int stack[999], top=-1;
void backtracking (vector& nums, vector& used) {
// 此时说明找到了一组
if (top+1 == nums.size()) {
vector ans;
for(int i=0;i<=top;i++){
ans.push_back(stack[i]);
}
result.push_back(ans);
return;
}
for (int i = 0; i < nums.size(); i++) {
// 已经使用过直接跳过即可
if(used[i] == true){
continue;
}
// used[i - 1] == true,说明同一树枝nums[i - 1]使用过
// used[i - 1] == false,说明同一树层nums[i - 1]使用过
// 如果同一树层nums[i - 1]使用过则直接跳过
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
continue;
}
used[i] = true;
stack[++top] = nums[i];
backtracking(nums, used);
top--;
used[i] = false;
}
}
public:
vector> permuteUnique(vector& nums) {
result.clear();
sort(nums.begin(), nums.end()); // 排序
vector used(nums.size(), false);
backtracking(nums, used);
return result;
}
};
从上面三个题目的代码可以看到其实这三个题目出自同一个模板,稍微总结一下就可以得出,也可以从上面三个题目很好的学习回溯的方法
上面两个组合和全排列都给出来C++和C语言的代码,C++主要借助vector容器的实现,而C则要复杂一些,我选择使用的是结构的方法实现。
解题思路和代码模板均来自代码随想录 - 力扣(LeetCode)
77. 组合
46. 全排列
47. 全排列 II