组合 - 三种形式

无重不可复选,可重不可复选,无重可复选

77. 组合 - 元素无重不可复选

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

题解

组合和子集是一样的:大小为 k 的组合就是节点数目为 k 的所有子集(回溯树第K层),取子集中第K层的节点。

参考:子集 - 三种形式

public List<List<Integer>> res = new LinkedList<>(); // 记录结果
public LinkedList<Integer> track = new LinkedList<>(); // 记录路径
public List<List<Integer>> combine(int n, int k) {
    // 从1开始
    backtrack(n, k, 1);
    return res;
}
public void backtrack(int n, int k, int start) {
    // 大小为 k 的组合就是大小为 k 的子集
    if (track.size() == k) {
        // 取子集中第K层的节点,遍历到了第 k 层,收集当前节点的值
        res.add(new LinkedList<>(track));
        return;
    }
    for (int i = start; i <= n; i++) {
        track.add(i); // 做选择
        backtrack(n, k, i + 1); // 下一次回溯树
        track.removeLast(); // 撤销选择
    }
}

变种:和为n的组合

/**
 * 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
 * 只使用数字1到9
 * 最多使用一次
 */
public int trackSum = 0; // 记录 track路径和
public void backtrack2(int n, int k, int start) {
    // 大小为 k 的组合就是大小为 k 的子集
    if (track.size() == k) {
        if (trackSum == n) { // 和为 n
            res.add(new LinkedList<>(track));
        }
        return;
    }
    for (int i = start; i <= 9; i++) {
        track.add(i);
        trackSum += i;
        backtrack2(n, k, i + 1);
        track.removeLast();
        trackSum -= i;
    }
}

40. 组合总和 II - 元素可重不可复选

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

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

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

题解

组合问题和子集问题是等价的,修改递归结束条件,使用 trackSum 记录回溯路径上的元素和,和target比较

  1. 先排序
  2. 再剪枝去重
public List<List<Integer>> res = new LinkedList<>(); // 记录结果
public LinkedList<Integer> track = new LinkedList<>(); // 记录路径
public int trackSum = 0; // 记录路径和
public List<List<Integer>> combinationSum2(int[] nums, int target) {
    // 先排序,让相同的元素靠在一起
    Arrays.sort(nums);
    // 开启回溯树,最初从下标0开始选择
    backtrack(nums, 0, target);
    return res;
}
public void backtrack(int[] nums, int start, int target) {
    // base case,达到目标和,找到符合条件的组合
    if (trackSum == target) {
        res.add(new LinkedList<>(track));
        // 已找到,递归终止,后面的trackSum > target,没必要再继续找了
        return;
    }
    // base case,超过目标和,递归终止
    if (trackSum > target) return;
    // 回溯算法标准框架
    for (int i = start; i < nums.length; i++) {
        // 剪枝
        if (i > start && nums[i] == nums[i - 1]) continue;
        track.add(nums[i]);
        trackSum += nums[i];
        // 通过 start 参数控制树枝的遍历,避免产生重复的子集
        backtrack(nums, i + 1, target);
        track.removeLast();
        trackSum -= nums[i];
    }
}

39. 组合总和 - 元素无重可复选

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

题解

是否重复使用元素, 通过start参数,控制树枝的遍历

  • start = start+1,下一层回溯树,不可以选自己(一个元素使用一次)
  • start = start,下一层回溯树,可以选自己(一个元素重复使用

设置合适的 base case 以结束算法,即路径和大于 target 结束。

public List<List<Integer>> res = new LinkedList<>(); // 记录结果
public LinkedList<Integer> track = new LinkedList<>(); // 记录回溯路径
public int trackSum = 0; // 记录 track 中的路径和
public List<List<Integer>> combinationSum(int[] nums, int target) {
    backtrack(nums, 0, target);
    return res;
}
// 回溯算法
public void backtrack(int[] nums, int start, int target) {
    // base case,找到目标和,记录结果,结束递归
    if (trackSum == target) {
        res.add(new LinkedList<>(track));
        return;
    }
    // base case,大于目标和,停止向下遍历,结束递归
    if (trackSum > target) {
        return;
    }
    // 回溯算法标准框架
    for (int i = start; i < nums.length; i++) {
        track.add(nums[i]);
        trackSum += nums[i];
        // 通过start参数,控制树枝的遍历
        // start = start,下一层回溯树,可以选自己(一个元素可以无限次使用)
        // start=start+1,下一层回溯树,不可以选自己(一个元素只使用一次)
        backtrack(nums, i, target);
        track.removeLast();
        trackSum -= nums[i];
    }
}
  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值