• 回溯去重问题


    第一个题:LC.39

    给一个无重复元素的正整数数组candidates和一个正整数target,找出所有和为target的唯一组合,数字可以重复选取

    1. class Solution {
    2. List<List<Integer>> res;
    3. public List<List<Integer>> combinationSum(int[] candidates, int target) {
    4. res = new ArrayList<>();
    5. Arrays.sort(candidates);
    6. backtrack(candidates,target,0,0,new ArrayList<>());
    7. return res;
    8. }
    9. public void backtrack(int[] candidates,int target,int index,int sum,List<Integer> list){
    10. if(sum == target){
    11. res.add(new ArrayList<>(list));
    12. return;
    13. }
    14. for(int i = index; i < candidates.length; i++){
    15. //if(i > 0 && candidates[i] == candidates[i-1]) continue;
    16. if(sum + candidates[i] > target) break;
    17. list.add(candidates[i]);
    18. backtrack(candidates,target,i,sum + candidates[i],list);
    19. list.remove(list.size() - 1);
    20. }
    21. }
    22. }

    这个题,题目当中说无重复元素,每个数可以无限取,所以不存在去重的问题,注释的那一行可以去掉了

    看下一个需要去重的题目:

    第二个题:LC.40

    题目和LC.39一样,唯一不同的条件是给的数组candidates含有重复元素。这个时候就需要去重了

    1. class Solution {
    2. List<List<Integer>> res;
    3. public List<List<Integer>> combinationSum2(int[] candidates, int target) {
    4. res = new ArrayList<>();
    5. Arrays.sort(candidates);
    6. backtrack(candidates,target,0,0,new ArrayList<>());
    7. return res;
    8. }
    9. public void backtrack(int[] candidates,int target,int index,int sum,List<Integer> list){
    10. if(target == sum){
    11. res.add(new ArrayList<>(list));
    12. return;
    13. }
    14. for(int i = index; i < candidates.length; i++){
    15. //在同一个for循环中,正在选当前层的元素
    16. //i >= index 的部分才是这一层可以选的元素,而不是i > 0的部分
    17. //同一层的元素不能重复而已
    18. if(i > index && candidates[i] == candidates[i-1]) continue;
    19. if(sum + candidates[i] > target) break;
    20. list.add(candidates[i]);
    21. backtrack(candidates,target,i+1,sum + candidates[i],list);
    22. list.remove(list.size() - 1);
    23. }
    24. }
    25. }

    什么时候需要去重呢?

    假设来到第一次循环,候选数组为:[1,2,3,3,4,5,6]

    我们来到第一次选择,这个时候可以选的数字有1,2,3,3,4,5,6

    注意在这一轮中,选择第一个3和第二个3,实际上是一样的,所以这里需要去重

    怎么去重?

    每一次调用回溯方法backtrack(),实际上就是进入了一轮选择

    在这一轮选择中,我们的起始位置是index

    那么在[index + 1 , nums.length - 1]这个区间内,重复的数字都要去掉

    就有了下面这一行判断

    这一行代表同一轮选择中的情况

    if(i > index && candidates[i] == candidates[i-1]) continue;

    第三个题:LC.46

    给一个不含重复数字的整数数组nums,返回其所有可能的全排列

    这个题加上一个boolean数组来判断是否选过该数字就可以了,因为数组没有重复数字

    1. class Solution {
    2. List<List<Integer>> res;
    3. public List<List<Integer>> permute(int[] nums) {
    4. int n = nums.length;
    5. res = new ArrayList<>();
    6. boolean[] isVisited = new boolean[n];
    7. backtrack(nums,isVisited,0,n,new ArrayList<>());
    8. return res;
    9. }
    10. public void backtrack(int[] nums,boolean[] isVisited,int count,int n,List<Integer> list){
    11. if(count == n){
    12. res.add(new ArrayList<>(list));
    13. return;
    14. }
    15. for(int i = 0; i < nums.length; i++){
    16. if(!isVisited[i]){
    17. list.add(nums[i]);
    18. isVisited[i] = true;
    19. backtrack(nums,isVisited,count+1,n,list);
    20. isVisited[i] = false;
    21. list.remove(list.size() - 1);
    22. }
    23. }
    24. }
    25. }

    第四个题:LC.47

    和第三题类似,唯一的区别就是数组中存在重复的数字

    什么时候会出现重复组合的情况?

    也是在同一轮次中,选择了相同的数字,就会造成重复

    怎么判断是否是相同轮次中?

    因为我们用到的是boolean数组来判断是否使用过这个数字

    那么,之前选择过的数字,isVisited[i]都变成了true

    现在没有选择过的数字,isVisited[i]就是false

    如果当前数字和前一个数字相同,并且前一个数字并没有被选取,说明他们处在同一轮选择的情况,要跳过

    if(i > 0 && nums[i] == nums[i-1] && !isVisited[i-1]) continue;

     

    题目代码:

    1. class Solution {
    2. List<List<Integer>> res;
    3. public List<List<Integer>> permuteUnique(int[] nums) {
    4. res = new ArrayList<>();
    5. Arrays.sort(nums);
    6. backtrack(nums,new boolean[nums.length],0,new ArrayList<>());
    7. return res;
    8. }
    9. public void backtrack(int[] nums,boolean[] isVisited,int count,List<Integer> list){
    10. if(count == nums.length){
    11. res.add(new ArrayList<>(list));
    12. return;
    13. }
    14. for(int i = 0; i < nums.length; i++){
    15. if(i > 0 && nums[i] == nums[i-1] && !isVisited[i-1]) continue;
    16. if(!isVisited[i]){
    17. list.add(nums[i]);
    18. isVisited[i] = true;
    19. backtrack(nums,isVisited,count+1,list);
    20. isVisited[i] = false;
    21. list.remove(list.size() - 1);
    22. }
    23. }
    24. }
    25. }

    总的来说,什么时候应该去重?

    同一轮次中出现重复数字的情况就应该去重

    对于总和组合和排列组合来说,同一轮次的情况是不同的

    总和组合的同一轮次,是[index , nums.length - 1]这个区间上

    而排列组合,同一轮次是isVisited[i]为false的数字

  • 相关阅读:
    MySQL实现的一点总结(六)
    如何验证 Kubernetes YAML 文件
    C语言指针操作(七)*指针数组和多重指针
    接口测试常用测试点
    力扣(LeetCode)1678. 设计 Goal 解析器(C++)
    【k8s资源调度-StatefulSet】
    【计算机网络实验】NAT配置实验
    亚马逊VS独立站Shopify
    使用阿里云国际版负载均衡管理多台服务器上的流量
    爬虫代理ip池创建【使用redis TTL实现】
  • 原文地址:https://blog.csdn.net/m0_50043893/article/details/125410387