第一题 39. Combination Sum
回溯即可。
var combinationSum = function(candidates, target) {
const res = [];
dfs(0,[],0);
return res;
function dfs(idx,tempArr,sum) {
if (sum===target) res.push(tempArr);
if (sum>=target||idx>=candidates.length) return;
dfs(idx+1,tempArr.slice(),sum);
for (let cnt=1; sum+candidates[idx]<=target; cnt++) {
tempArr.push(candidates[idx]);
sum += candidates[idx];
dfs(idx+1,tempArr.slice(),sum);
}
}
};
第二题 40. Combination Sum II
一种思路是参考上一题。上一题中没有对每个元素使用次数做限制,这一题中对每个元素使用次数做了限制,一个元素的最多使用次数为该元素在candidates数组中的出现次数。因此我们统计出每个元素的出现次数,然后在dfs中对该元素遍历过程中令cnt不超过该出现次数。
var combinationSum2 = function(candidates, target) {
const num_cnt = {};
for (const num of candidates) {
if (num in num_cnt) num_cnt[num]++;
else num_cnt[num] = 1;
}
const nums = Object.keys(num_cnt);
const res = [];
dfs(0,[],0);
return res;
function dfs(idx,tempArr,sum) {
if (sum===target) res.push(tempArr.slice());
if (sum>=target||idx>=nums.length) return;
dfs(idx+1,tempArr.slice(),sum);
for (let cnt=1; cnt<=num_cnt[nums[idx]]&&sum<=target; cnt++) {
sum += parseInt(nums[idx]);
tempArr.push(parseInt(nums[idx]));
dfs(idx+1,tempArr.slice(),sum);
}
}
};
另一种思路,对于dfs的每一层,都看作从当前idx开始往后统计target-sum的集合。因此我们在dfs的每一层中对i遍历时,如果candidates[i]===candidates[i-1],则说明以candidates[i]这个数开头的所有可能的组合结果都遍历过了,因此直接跳过。
var combinationSum2 = function(candidates, target) {
candidates.sort((a,b)=>a-b);
const res = [];
dfs(0,[],0);
return res;
function dfs(idx,tempArr,sum) {
if (sum===target) res.push(tempArr.slice());
if (sum>=target||idx>=candidates.length) return;
for (let i = idx; i < candidates.length; i++) {
if (i>idx&&candidates[i]===candidates[i-1]) continue;
dfs(i+1,tempArr.concat([candidates[i]]),sum+candidates[i]);
}
}
};
第三题 216. Combination Sum III
思路很清晰,与上一题40. Combination Sum II很像。
var combinationSum3 = function(k, n) {
const res = [];
dfs(1,[],0);
return res;
function dfs(num,tempArr,sum) {
if (sum===n&&tempArr.length===k) res.push(tempArr.slice());
if (sum>=n||tempArr.length>=k||num>9) return;
for (let i = num; i <= 9&&sum+i <= n; i++) {
dfs(i+1,tempArr.concat([i]),sum+i);
}
}
};
第四题 377. Combination Sum IV
这一题,如果继续使用dfs会超时。而且这题只要求计算组合种数,没要求列出所有可能的组合情况,因此我们考虑使用动态规划。令dp[t]表示target等于t时的组合种数,dp[t]=sum(dp[t-num])。
var combinationSum4 = function(nums, target) {
const dp = new Array(target+1).fill(0);
nums.sort((a,b)=>a-b);
dp[0] = 1;
for (let t = 1; t <= target; t++) {
for (const num of nums) {
if (num>t) break;
dp[t] += dp[t-num];
}
}
return dp[target];
};
总结
从上可以看出,对于组合类型问题,大多使用dfs回溯,特别是对于需要列出所有组合情况的问题,一般这类题的数据量不会太大。如果问题变成了求组合种数,且数据量较大的情况,我们一般考虑使用动态规划解决。