回溯法解决组合问题(慢更新...)

        用回溯法来解决组合问题,本质上也是一种暴力搜索的方式。它其实可以看做是一种递归搜索,在组合问题中,我们就是在每一层选中一个或多个元素,然后不断递归,直至找到满足条件的组合。然后在过程中不断进行回溯,我个人感觉也可以用 释放 来表明其含义 。

        在carl哥的代码随想录中给出了一张图非常的通俗易懂 ,里面讲解的也更加全面,大家可以去参考里面的讲解内容,个人感觉还是非常有帮助的。 

        

  • 力扣(LeetCode)22 括号生成

        数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

    

class Solution {
      StringBuilder path = new StringBuilder();
      List<String> res = new ArrayList<>();
      public List<String> generateParenthesis(int n) {
         back(n,0,0);
         return res;
      }

      public void back(int n,int left,int right){
         if(left+right == n*2){
            res.add(path.toString());
            return;
         }

         if(left < n){
            path.append("(");
            back(n,left+1,right);
         //进行回溯
            path.deleteCharAt(path.length()-1);
         }

         if(right < left){
            path.append(")");
            back(n,left,right+1);
         //进行回溯
            path.deleteCharAt(path.length()-1);
         }
      }
   }
  • 力扣(LeetCode)77 组合

        给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。你可以按 任何顺序 返回答案。

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        fun(n,k,1);
        return result;
    }

    public void fun(int n,int k,int start){
        if(path.size() == k){
            result.add(new ArrayList<>(path));
            return;
        }
    
        for(int i= start; i <= n - (k - path.size()) + 1;i++){
            path.add(i);
            fun(n,k,i+1);
            path.removeLast();
        }
    }
}
  • 力扣(LeetCode)39 组合总和

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

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

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

class Solution {
    List<List<Integer>> result = new ArrayList<>();
        LinkedList<Integer> path = new LinkedList<>();
        public List<List<Integer>> combinationSum(int[] candidates, int target) {
            Arrays.sort(candidates);
            fun(candidates,target,0,0);
            return result;
        }

        public void fun(int[] candidates,int target,int sum,int idx){
            if(sum == target){
                result.add(new ArrayList<>(path));
                return;
            }

            for(int i=idx;i<candidates.length;i++){
                
                if(sum + candidates[i]>target) break;
                path.add(candidates[i]);

                fun(candidates,target,sum+candidates[i],i);
                path.remove(path.size()-1);
            }
        }
}
  •  力扣(LeetCode)40 组合总和2

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

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

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

class Solution {
    List<List<Integer>> res = new ArrayList<>();
        LinkedList<Integer> path = new LinkedList<>();
        boolean[] used;
        public List<List<Integer>> combinationSum2(int[] candidates, int target) {
            used = new boolean[candidates.length];
            Arrays.sort(candidates);
            fun(candidates,target,0,0);
            return res;
        }

        public void fun(int[] candidates,int target,int sum,int start){
            if(sum == target){
                res.add(new ArrayList<>(path));
                
            }

            for(int i=start;i<candidates.length ;i++){
                if(sum + candidates[i] > target)break;
                if(i>0&&candidates[i] == candidates[i-1] && used[i-1] == false) continue;

                path.add(candidates[i]);
                sum+=candidates[i];
                used[i] = true;
                fun(candidates,target,sum,i+1);
                sum-=candidates[i];
                path.removeLast();
                used[i] = false;
            }

        }
    }

  •   力扣(LeetCode)216 组合总和3

        找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

        只使用数字1到9,每个数字 最多使用一次 ,返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        fun(n,k,1,0);
        return result;
    }
    public void fun(int target,int k,int start,int sum){
        if(sum  >target ) return;

        if(path.size() == k){
            if(sum == target){
                result.add(new ArrayList<>(path));
                return;
            }
        }

        for(int i=start;i<=9-(k-path.size())+1;i++){
            path.add(i);
            sum+=i;
            fun(target,k,i+1,sum);
            path.removeLast();
            sum-=i;
        }
    }
}
  • 力扣(LeetCode)377 组合总和4

        给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。

        题目数据保证答案符合 32 位整数范围。

        这题看起来和上面的题目长得差不多,但是因为这题没有重复的限制,并且一个元素可以重复使用,那么对于nums中各个元素较小且所求的target较大时,所需要的递归次数就非常吓人了。

       我在自己电脑上运行了下面这段用基本回溯思想写的代码,实例是nums = [2,1,3]  target = 35,所运行的时间达到了14秒多,肯定是超时了。上面的数字是组合的种类

class Solution {
      int sum = 0;
      int res = 0;
      public int combinationSum4(int[] nums, int target) {
         fun(nums,target);
         return res;
      }

      public void fun(int[] nums,int target){
         if(sum > target){
            return;
         }
         if(sum == target){
            res++;
            return;
         }

         for(int i = 0;i<nums.length;i++){
            sum+=nums[i];
            fun(nums,target);
            sum-=nums[i];
         }
      }
   }

         这题可以用动归的思想来做,虽然很难想到。

class Solution {
    public int combinationSum4(int[] nums, int target) {
        int len = nums.length;
            int[] dp = new int[target+1];
            dp[0] = 1;
            for(int i = 0;i<=target;i++)
                for(int j = 0;j<len;j++){
                    if(i>=nums[j]){
                        dp[i]+= dp[i-nums[j]];
                    }
                }
            return dp[target];
    }
}

仅作为个人笔记使用,如有错误还需海涵。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值