刷题日记06《回溯算法》

问题描述

力扣https://leetcode.cn/problems/Ygoe9J/

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

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

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

示例 1:

输入: candidates = [2,3,6,7], target = 7
输出: [[7],[2,2,3]]
示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:

输入: candidates = [2], target = 1
输出: []
示例 4:

输入: candidates = [1], target = 1
输出: [[1]]
示例 5:

输入: candidates = [1], target = 2
输出: [[1,1]]

 

提示:

1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都是独一无二的。
1 <= target <= 500

解题思路

此题是回溯算法中无重复但每个数字可以多次选择的题目,如果对每个数字都进行无选择的递归的话,结果一定是栈溢出的,所以我们要选择合适的剪枝策略来优化递归过程,我们选择的优化策略如下:如果当前下标的数组的值大于target,直接将这种结果剪掉,如果小于等于的话,进行下一步的递归,并同时更新target,如果最终target==0,直接将此种结果加入到最终结果中,这层递归结束,如果index==数组长度,递归结束

实例代码

class Solution {
    private LinkedList<List<Integer>>res=new LinkedList<>();
    private LinkedList<Integer>data=new LinkedList<>();
    private int sum=0;
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        reverse(candidates,target,0);
        return res;
    }
    public void reverse(int []candidates,int target,int index){
        //递归出口
        if(index==candidates.length){
            return;
        }
        if(target==0){
            res.add(new LinkedList(data));
            return;
        }
        //不选择当前数字
        reverse(candidates,target,index+1);
        //选择当前数字
        if(target-candidates[index]>=0){
            data.add(candidates[index]);
            reverse(candidates,target-candidates[index],index);
            //回溯
            data.removeLast();
        }
      
    }
}

问题描述

力扣icon-default.png?t=N658https://leetcode.cn/problems/4sjJUc/

给定一个可能有重复数字的整数数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次,解集不能包含重复的组合。 

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

 

提示:

1 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30

解题思路

此题与上文中的题目的条件恰好相反,此题是选择有重复数字,但是每个数字只能选择一次,在这种条件下, 我们必须将相同的数字进行剪枝,必然需要涉及到使重复的数字相邻,(将数组进行排序即可),那么回溯的过程中我们又该如何去除相邻相同数字的组合呢?在递归函数中加入循环即可(需要思想的转化,在递归函数中加入循环,从index(当前指标)的位置直到数组的最后,递归到的每个数组位置都需要加入数据集中,与我们单纯使用下标进行递归的过程是完全一致的),所以当数组相邻元素相等时(nums[i]==nums[i-1]),在直接跳过这个数字即可(因为上一层循环已经用过该数字了),这样进行递归和回溯,直到target==0或者走到数组最后一个元素后方

实例代码

class Solution {
    private LinkedList<List<Integer>>res=new LinkedList<>();
    private LinkedList<Integer>data=new LinkedList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        reverse(candidates,0,target);

        return res;
    }
    public void reverse(int[]candidates,int index,int target){
        if(target==0){
                res.add(new LinkedList(data));
                return;
            }
          for(int i=index;i<candidates.length;++i){
              if(candidates[i]>target){
                  break;
              }
              if(i>index&&candidates[i]==candidates[i-1]){
                  continue;
              }
              data.add(candidates[i]);
              reverse(candidates,i+1,target-candidates[i]);
              data.removeLast();
          }
    }
}

猜你喜欢

转载自blog.csdn.net/m0_65431718/article/details/131587303
今日推荐