组合问题:N个数⾥⾯按⼀定规则找出组合

组合问题:N个数⾥⾯按⼀定规则找出k个数的集合

回溯问题其他文章(组合,分割,子集,排列)

给定两个整数 n k,返回 1 ... n 中所有可能的 k 个数的组合。

k==n,只有一个结果。因为组合不考虑顺序。
如果是返回结果有多少种,利用组合公式即可。

class Solution {

        List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> combine(int n, int k) {
            if (k <= 0 || n < k) {
                return res;
            }
            Deque<Integer> path = new ArrayDeque<>();
            dfs(n, k, 1, path);
            return res;
        }

        private void dfs(int n, int k, int begin, Deque<Integer> path) {
            // 递归终止条件是:path 的长度等于 k
            if (path.size() == k) {
                res.add(new ArrayList<>(path));
                return;
            }
            // 遍历可能的搜索起点
//            for (int i = begin; i <= n; i++) {
                // 剪枝后   代码时间16ms -> 1ms
//                剪枝代码
            for (int i = begin; i <=  n - (k - path.size()) + 1; i++) {
                // 向路径变量里添加一个数
                path.addLast(i);
                // 下一轮搜索,设置的搜索起点要加 1,因为组合数理不允许出现重复的元素
                dfs(n, k, i + 1, path);
                // 重点理解这里:深度优先遍历有回头的过程,因此递归之前做了什么,递归之后需要做相同操作的逆向操作
                path.removeLast();
            }
        }

    }

(k - path.size())  代表还有几位数就凑够k位数了 用来剪枝。

找出所有相加之和为 n k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

本题就是在 [1,2,3,4,5,6,7,8,9] 这个集合中找到和为 n k 个数的组合。因为有和的限制,所以加一个sum判断,因为是正整数 所以可以有两处剪枝的地方。
class Solution {

        List<List<Integer>> res = new ArrayList<>();
        public List<List<Integer>> combinationSum3(int k, int n) {
            if (n <= 0 || n > 45) {
                return res;
            }
            Deque<Integer> path = new ArrayDeque<>();
            dfs(n, k, 0, 1, path);
            return res;
        }
        private void dfs(int target, int k, int sum, int begin, Deque<Integer> path) {
            if(sum > target) return; // 剪枝
            if (path.size() == k) {
                if(sum == target){
                    res.add(new ArrayList<>(path));
                    return;
                }
            }
            for (int i = begin; i <=  9 - (k - path.size()) + 1; i++) {// 剪枝
                path.addLast(i);
                sum += i;
                dfs(target, k, sum, i + 1, path);
                sum -= i;
                path.removeLast();
            }
        }

    }

 电话号码

 这道题需要收集树的所有子节点的值,无法剪枝。正常回溯即可。

class Solution {
        List<String> res = new ArrayList<>();
        String[] strs = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        public List<String> letterCombinations(String digits) {
            int n = digits.length();
            if(n==0) return res;
            StringBuilder s = new StringBuilder();
            dfs(digits,0,n,s);
            return res;
        }

        public void dfs(String digits,int index,int n,StringBuilder s){
            if(index==n){
                res.add(s.toString());
                return;
            }
            int num = digits.charAt(index) - '0';
            String str = strs[num];//'abc'
            for (int i = 0; i < str.length(); i++) {
                s.append(str.substring(i,i+1));
                dfs(digits,index+1,n,s);
                s.replace(s.length()-1,s.length(),"");
            }

        }
    }

给定⼀个⽆重复元素的数组 candidates 和⼀个⽬标数 target ,找出 candidates 中所有可以使数字和为
target 的组合。

candidates 中的数字可以⽆限制重复被选取

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        int len = candidates.length;
        List<List<Integer>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }
        // 排序是剪枝的前提
        Arrays.sort(candidates);
        Deque<Integer> path = new ArrayDeque<>();
        dfs(candidates, 0, len, target, path, res);
        return res;
    }

    private void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res) {
        // 由于进入更深层的时候,小于 0 的部分被剪枝,因此递归终止条件值只判断等于 0 的情况
        if (target == 0) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = begin; i < len; i++) {
            // 重点理解这里剪枝,前提是候选数组已经有序,
            if (target - candidates[i] < 0) {
                break;// 当前这一支全都pass
            }
            path.addLast(candidates[i]);// path存放当前已经选择的队列
            dfs(candidates, i, len, target - candidates[i], path, res);
            path.removeLast();
        }
    }


}

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

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        if (candidates.length == 0) {
            return res;
        }
        // 排序是剪枝的前提
        Arrays.sort(candidates);
        Deque<Integer> path = new ArrayDeque<>();
        dfs(candidates, 0, target, path, res);
        return res;
    }

    private void dfs(int[] candidates, int begin, int target, Deque<Integer> path, List<List<Integer>> res) {
        // 由于进入更深层的时候,小于 0 的部分被剪枝,因此递归终止条件值只判断等于 0 的情况
        if (target == 0) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = begin; i < candidates.length; i++) {
            // 重点理解这里剪枝,前提是候选数组已经有序,
            if (target - candidates[i] < 0) {
                break;// 当前这一支全都pass
            }
            if(i>begin && candidates[i] == candidates[i-1] ){
                continue;//这样就不会有重复的组合 
            } // i>begin 不能省略,因为 1 1 2 这种情况同一个路径上 第一个数字不需要判断
            path.addLast(candidates[i]);// path存放当前已经选择的队列
            dfs(candidates, i+1, target - candidates[i], path, res);
            path.removeLast();
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

trigger333

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值