「前端刷题」40. 组合总和 II

299 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

题目

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

**注意:**解集不能包含重复的组合。 

 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8, 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5, 输出: [ [1,2,2], [5] ]

 

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

思路

回溯+减枝

和39题的区别

  • 数组含有重复数字
  • 每个数字只能选择一次

代码

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum2 = function(candidates, target) {
    if (candidates.length < 0) {
        return [];
    }
    
    // 排序
    candidates.sort((a, b) => parseInt(a) - parseInt(b));

    this.backtrack = function(candidates, list, start, target) {
        if (target == 0) {
            res.push(list.slice());
            return;
        }

        for (let i = start; i < candidates.length; i++) {
            // 减枝:因为target小于candidates[i],candidates升序,所以candidates从i+1后面的数肯定大于target,直接break 进行减枝
            if (target < candidates[i]) {
                break;
            }

            // 减枝:跳过重复的数字,重复的不选择 
            if (i > start && candidates[i] == candidates[i-1]) {
                continue;
            }

            list.push(candidates[i]);
            
            // i位置的选择的,所以从i+1开始选
            backtrack(candidates, list, i + 1, target - candidates[i]);

            list.pop();
        }
    }

    let res = [];
    let list = [];
    backtrack(candidates, list, 0, target);
    return res;
};

复杂度

  • 时间O(n*2^n)
  • 空间O(target),取决于递归的栈深度

思路2

最近的每日一题基本都是递归回溯型,相比组合总数这题,多了一个不能重复使用数字的条件。故首先对数组进行排序,使得重复的数字相邻,若当前数字等于前一个数字则跳出循环进入下一个数字。

代码

/**
 * @param {number[]} candidates
 * @param {number} target
 * @return {number[][]}
 */
var combinationSum2 = function(candidates, target) {
    var res = [], temp = []; //res保存结果,temp保存当前选择的数字
    var sum = 0;    //当前数字和
    var len = candidates.length;
    candidates.sort((a, b) => { return a - b}); //排序
    dfs(0);
    return res;
    function dfs(k) {
        if(sum >= target) {
            if(sum == target) {
                res.push(temp.slice()); //满足条件,复制一份放入结果
            }
            return;
        }
        for(let i = k; i < len; i++) {
            if(target < candidates[i])  break; //如果当前将要值已经大于目标值直接退出循环
            if(i > k && candidates[i] == candidates[i - 1]) continue; //重复的情况
            sum += candidates[i];
            temp.push(candidates[i]);
            dfs(i + 1); //递归
            temp.pop(); //回溯
            sum -= candidates[i];
        }
    }
};