【算法题】回溯算法相关学习总结-1

93 阅读2分钟

冲冲冲,做完今天的题,后面开始重新看juc包源码!

leetcode 40. 组合总和2

image.png

思路

  • 还是用回溯,但是用set去重的话超时,
  • 接下来看了下大佬的思路,如果我们对数组排序:为1,1,7;target=8,我们新增一个start值,记录是从哪个数组下标开始的,

关键过程:

  • start=0,i=start=0,此时stack选择了1,sum=1<8;
    • 递归进入,start=1,i=1,此时stack1,1,sum=2<8
      • 递归进入,start=2,i=2,此时stack=1,1,7,sum=9>8, 于是开始回溯
    • 回溯到上一层 sum - 7 = 2, stack退出7,变成1,1
    • 继续for循环,i++=2,此时stack1,7,sum=8,
      • 加入结果集,RETURN
    • 回溯到上一层 sum - 1 = 1, stack=1,此时i到len-1了,回溯到上一层
  • 回溯到第一层, 关键来了!start=0,stack为null,i此时++了,应该是1,但是数组1=数组0,如果我们选择1,那么就可能存在重复接了!!
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    int sum = 0;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        // 先进行排序
        Arrays.sort(candidates);
        backtrack(0, candidates, target);
        return res;
    }

    public void backtrack(int start, int[] candidates, int target) {
        if (sum == target) {
            res.add(new ArrayList<>(path));
            return;
        }
        // 剪枝操作
        for (int i = start; i < candidates.length && sum + candidates[i] <= target; ++i) {
            // 去重
            if (i > start && candidates[i] == candidates[i - 1]) continue;
            path.add(candidates[i]);
            sum += candidates[i];
            backtrack(i+1, candidates, target);
            sum -= candidates[i];
            path.remove(path.size() - 1);
        }
    }
}

leetcode 39. 组合总和

image.png

思路

  • 很明显要用到回溯算法(循环+递归),但是难点在如何去重?
  • 第一感觉用Set,这种也好写,就是很慢,不过这道题能ac出来
class Solution {
   // huisu,set去重,92ms,超过5%
   public List<List<Integer>> combinationSum(int[] candidates, int target) {
       List<Integer> stack = new ArrayList<>();
       Set<List<Integer>> result = new HashSet<>();

       int sum = 0;
       huisu(candidates, target, stack, result, 0);
       Arrays.sort(candidates);
       List<List<Integer>> result2 = new ArrayList<>();
       for (List<Integer> list : result){
           result2.add(list);
       }
       return result2;
   }

   private void huisu(int[] candidates, int target, List<Integer> stack, Set<List<Integer>> result, int sum) {
       if (sum == target){
           List<Integer> arr = new ArrayList<>(stack);
           Collections.sort(arr);
           result.add(arr);
           return;}
       if (sum > target){return;}
       for (int i =0;i<candidates.length;i++){
           stack.add(candidates[i]);
           sum += candidates[i];
           huisu(candidates,target,stack,result,sum);
           sum -= candidates[i];
           stack.remove(stack.size()-1);
       }
   }
}
  • 参考上面一题的去重思路,这道题因为是可以重复使用数字,所以我们start不主动+1
// huisu
public List<List<Integer>> combinationSum(int[] candidates, int target) {
    List<Integer> stack = new ArrayList<>();
    List<List<Integer>> result = new ArrayList<>();

    int sum = 0;
    Arrays.sort(candidates);
    huisu(candidates, target, stack, result,0, 0);
    return result;
}

private void huisu(int[] candidates, int target, List<Integer> stack, List<List<Integer>> result,int start, int sum) {
    if (sum == target){
        List<Integer> arr = new ArrayList<>(stack);
        result.add(arr);
        return;}
    if (sum > target){return;}
    for (int i =start;i<candidates.length;i++){
        stack.add(candidates[i]);
        sum += candidates[i];
        huisu(candidates,target,stack,result,i,sum);
        sum -= candidates[i];
        stack.remove(stack.size()-1);
    }
}