回溯算法套路②组合型回溯+剪枝

思路转换:从n个数中选k个数的组合可以看成是长度固定的子集,可以转化为子集型回溯+路径长度判断。
在这里插入图片描述
优化:可以进行剪枝,设path长为m,那么还需要选d=k - m个数,设当前需要从[1,i]这i个数中选数如果i<d,最后必然无法选出k个数不需要继续递归,这是一种剪枝技巧。

例题:77. 组合

class Solution {
    private int k;
    private final List<Integer> path = new ArrayList<>();
    private final List<List<Integer>> ans = new ArrayList<>();

    public List<List<Integer>> combine(int n, int k) {
        this.k = k;
        dfs(n);
        return ans;
    }

    private void dfs(int i) {
        int d = k - path.size(); // 还要选 d 个数
        if (d == 0) {
            ans.add(new ArrayList<>(path));
            return;
        }
        for (int j = i; j >= d; --j) {
            path.add(j);
            dfs(j - 1);
            path.remove(path.size() - 1);
        }
    }
}

时间复杂度:分析回溯问题的时间复杂度,有一个通用公式:路径长度×搜索树的叶子数。对于本题,它等于o(k·C(n,k))

例题:例题:77. 组合

此题相比于上一题基本上就是增加了两个剪枝条件:
当所选数值大于目标值时,直接返回;
当所能选择的最大值小于目标值时,直接返回。(该值可以用求和公式)∑=(首数值+末数值)×(数列个数/2)

代码如下:

class Solution {
    private int k;
    private final List<Integer> path = new ArrayList<>();
    private final List<List<Integer>> ans = new ArrayList<>();

    public List<List<Integer>> combinationSum3(int k, int n) {
        this.k = k;
        dfs(9, n);
        return ans;
    }

    private void dfs(int i, int t) {
        int d = k - path.size(); // 还要选 d 个数
        if (t < 0 || t > (i * 2 - d + 1) * d / 2) // 剪枝
            return;
        if (d == 0) {
            ans.add(new ArrayList<>(path));
            return;
        }
        for (int j = i; j >= d; --j) {
            path.add(j);
            dfs(j - 1, t - j);
            path.remove(path.size() - 1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值