内容介绍
给你一个 无重复元素 的整数数组
candidates
和一个目标整数target
,找出candidates
中可以使数字和为目标数target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。对于给定的输入,保证和为
target
的不同组合数少于150
个。示例 1:
输入:candidates =[2,3,6,7],
target =7
输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。示例 2:
输入: candidates = [2,3,5],
target = 8 输出: [[2,2,2,2],[2,3,3],[3,5]]示例 3:
输入: candidates =[2],
target = 1 输出: []提示:
1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates
的所有元素 互不相同1 <= target <= 40
完整代码
int candidatesSize_tmp;
int ansSize;
int combineSize;
int* ansColumnSize;
void dfs(int* candidates, int target, int** ans, int* combine, int idx) {
if (idx == candidatesSize_tmp) {
return;
}
if (target == 0) {
int* tmp = malloc(sizeof(int) * combineSize);
for (int i = 0; i < combineSize; ++i) {
tmp[i] = combine[i];
}
ans[ansSize] = tmp;
ansColumnSize[ansSize++] = combineSize;
return;
}
// 直接跳过
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
combine[combineSize++] = candidates[idx];
dfs(candidates, target - candidates[idx], ans, combine, idx);
combineSize--;
}
}
int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes) {
candidatesSize_tmp = candidatesSize;
ansSize = combineSize = 0;
int** ans = malloc(sizeof(int*) * 1001);
ansColumnSize = malloc(sizeof(int) * 1001);
int combine[2001];
dfs(candidates, target, ans, combine, 0);
*returnSize = ansSize;
*returnColumnSizes = ansColumnSize;
return ans;
}
思路详解
问题概述
组合总和问题是一个典型的回溯算法问题,其目标是从候选数组中找出所有可能的组合,这些组合的和等于给定的目标值。
代码详解
int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes) {
// 初始化一些变量
candidatesSize_tmp = candidatesSize; // 用于保存候选数组的大小
ansSize = combineSize = 0; // 初始化结果数组的大小和当前组合的大小
int** ans = malloc(sizeof(int*) * 1001); // 分配结果数组的空间
ansColumnSize = malloc(sizeof(int) * 1001); // 分配结果数组列大小的空间
int combine[2001]; // 初始化组合数组
dfs(candidates, target, ans, combine, 0); // 开始递归查找组合
*returnSize = ansSize; // 设置返回的结果数组大小
*returnColumnSizes = ansColumnSize; // 设置返回的结果数组列大小
return ans; // 返回结果数组
}
void dfs(int* candidates, int target, int** ans, int* combine, int idx) {
// 如果索引已经达到候选数组的大小,说明当前路径已经遍历完成
if (idx == candidatesSize_tmp) {
return;
}
// 如果当前目标值等于0,说明找到了一个有效的组合
if (target == 0) {
int* tmp = malloc(sizeof(int) * combineSize); // 创建一个临时数组来保存当前组合
for (int i = 0; i < combineSize; ++i) {
tmp[i] = combine[i]; // 复制当前组合到临时数组
}
ans[ansSize] = tmp; // 将临时数组添加到结果数组中
ansColumnSize[ansSize++] = combineSize; // 更新结果数组列大小
return; // 返回上一层递归
}
// 如果不满足直接跳过当前元素
dfs(candidates, target, ans, combine, idx + 1);
// 如果当前元素小于目标值,可以选择当前元素
if (target - candidates[idx] >= 0) {
combine[combineSize++] = candidates[idx]; // 将当前元素添加到组合中
dfs(candidates, target - candidates[idx], ans, combine, idx); // 递归查找剩余的组合
combineSize--; // 回溯,撤销当前元素的选择
}
}
关键步骤解释
-
初始化:
- 在
combinationSum
函数中,首先初始化一些变量,包括结果数组的大小和当前组合的大小。
- 在
-
递归函数定义:
dfs
函数是递归函数,它接受候选数组、目标值、结果数组、当前组合数组和当前索引作为参数。
-
递归终止条件:
- 如果索引已经达到候选数组的大小,说明当前路径已经遍历完成,函数返回。
- 如果当前目标值等于0,说明找到了一个有效的组合,函数将当前组合保存到结果数组中,并返回。
-
递归调用:
- 如果不满足直接跳过当前元素的条件,函数递归调用自身,尝试下一个元素。
- 如果当前元素小于目标值,函数将当前元素添加到组合中,递归调用自身,尝试剩余的组合。
- 回溯时,撤销当前元素的选择,继续递归查找其他可能的组合。
-
结果保存:
- 在递归函数中,当找到一个有效的组合时,将当前组合保存到结果数组中,并更新结果数组的大小。
-
返回结果:
- 在
combinationSum
函数中,返回结果数组和结果数组列大小。
- 在
知识点精炼
-
递归算法:
- 使用递归方法
dfs
来遍历所有可能的组合。
- 使用递归方法
-
回溯算法:
- 在
dfs
函数中应用回溯,撤销当前元素的选择以探索其他可能性。
- 在
-
数组与指针:
- 使用数组
combine
来存储当前组合的元素,使用指针combineSize
来跟踪组合的大小。
- 使用数组
-
动态内存分配:
- 使用
malloc
为结果数组和临时数组分配内存。
- 使用
-
状态更新:
- 在递归函数中,使用
combineSize
和ansSize
来跟踪当前组合的大小和结果数组的大小。
- 在递归函数中,使用
-
数组索引操作:
- 通过数组索引来访问和更新当前组合和结果数组。
-
条件判断:
- 使用条件判断来确定是否跳过当前元素或者将当前元素添加到组合中。
-
子序列计数:
- 通过
combineSize
来计算当前组合的大小。
- 通过
-
结果保存:
- 将有效的组合保存到结果数组中,并更新结果数组的大小。
-
返回值:
- 最终返回结果数组和结果数组列大小。
通过这些知识点,代码有效地解决了“组合总和”问题,展示了递归、回溯、以及高效状态管理的算法技巧。