返回
「前端刷题」39. 组合总和:踏上数字组合的探索之旅
前端
2024-02-16 18:28:19
在计算机科学领域,组合总和是一个经典的动态规划问题。给定一个正整数数组candidates
和一个正整数target
,我们需要在candidates
中找出所有可以使数字和为target
的组合。
动态规划算法解析
// 方法一:回溯搜索法
function combinationSum(candidates, target) {
// 结果数组
const result = [];
// 回溯函数
const backtrack = (index, currentCombination, currentSum) => {
// 如果超过目标值,则直接返回
if (currentSum > target) {
return;
}
// 如果等于目标值,则将当前组合加入结果数组
if (currentSum === target) {
result.push([...currentCombination]);
return;
}
// 如果还没有达到目标值,则继续回溯
for (let i = index; i < candidates.length; i++) {
// 将当前数字加入当前组合
currentCombination.push(candidates[i]);
// 计算当前和
currentSum += candidates[i];
// 递归调用回溯函数
backtrack(i, currentCombination, currentSum);
// 从当前组合中移除当前数字
currentCombination.pop();
// 将当前和减去当前数字
currentSum -= candidates[i];
}
};
// 从第一个数字开始回溯
backtrack(0, [], 0);
return result;
}
这个算法的时间复杂度是O(2^n),其中n是candidates
数组的长度。这是因为对于每个数字,我们都有两个选择:选择它或不选择它。因此,对于n个数字,就有2^n种可能的组合。
// 方法二:动态规划法
function combinationSum(candidates, target) {
// 创建一个二维数组dp,dp[i][j]表示使用candidates[0]到candidates[i-1]这些数字能否凑出j这个数
const dp = new Array(candidates.length + 1).fill(0).map(() => new Array(target + 1).fill(false));
// 初始化dp[0][0]为true,表示空集可以凑出0这个数
dp[0][0] = true;
// 遍历candidates数组
for (let i = 1; i <= candidates.length; i++) {
// 遍历target数组
for (let j = 1; j <= target; j++) {
// 如果不使用candidates[i-1]这个数字,则dp[i][j]等于dp[i-1][j]
dp[i][j] = dp[i - 1][j];
// 如果使用candidates[i-1]这个数字,则dp[i][j]等于dp[i-1][j-candidates[i-1]]
if (j >= candidates[i - 1]) {
dp[i][j] |= dp[i - 1][j - candidates[i - 1]];
}
}
}
// 结果数组
const result = [];
// 从后往前遍历dp数组,找到所有为true的dp[i][j]
for (let i = candidates.length; i >= 1; i--) {
for (let j = target; j >= 0; j--) {
// 如果dp[i][j]为true,则表示使用candidates[0]到candidates[i-1]这些数字可以凑出j这个数
if (dp[i][j]) {
// 将candidates[i-1]加入结果数组
result.push(candidates[i - 1]);
// 将j减去candidates[i-1]
j -= candidates[i - 1];
// 继续从后往前遍历dp数组,找到下一个为true的dp[i][j]
}
}
}
return result;
}
这个算法的时间复杂度是O(n * target),其中n是candidates
数组的长度,target是目标值。这是因为对于每个数字,我们都需要检查它是否能和前面的数字组合起来凑出目标值。
复杂度分析
在第一种方法中,我们使用回溯搜索法来解决组合总和问题。回溯搜索法的复杂度为O(2^n),其中n是candidates
数组的长度。这是因为对于每个数字,我们都有两个选择:选择它或不选择它。因此,对于n个数字,就有2^n种可能的组合。
在第二种方法中,我们使用动态规划法来解决组合总和问题。动态规划法的复杂度为O(n * target),其中n是candidates
数组的长度,target是目标值。这是因为对于每个数字,我们都需要检查它是否能和前面的数字组合起来凑出目标值。
结语
组合总和问题是一个经典的动态规划问题,它可以有多种不同的解法。我们介绍了两种常见的解法:回溯搜索法和动态规划法。这两种方法各有优劣,回溯搜索法的时间复杂度较高,但易于理解和实现;动态规划法的时间复杂度较低,但实现起来比较复杂。