代码随想录算法训练营Day23

组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

class Solution {  
    List<List<Integer>> result = new ArrayList<>();  
    List<Integer> path = new ArrayList<>();  
    public List<List<Integer>> combinationSum(int[] candidates, int target) {  
        Arrays.sort(candidates);//剪枝
        backingtracking(candidates,target,0,0);  
        return result;  
    }  
    public void backingtracking(int[] candidates, int target,int startIndex,int sum){  
        if(sum>target){  
            return;  
        }  
        if(sum == target){  
            //result.add(path);这里是错误的!  
            result.add(new ArrayList<>(path));  
            return;  
        }   
        for(int i = startIndex;i<candidates.length;i++){  
            if(sum+candidates[i]>target) break;//剪枝
            sum+=candidates[i];  
            path.add(candidates[i]);  
            backingtracking(candidates,target,i,sum);//这里不能传入i+1,因为可以重复使用!  
            sum-=candidates[i];  
            path.remove(path.size()-1);  
        }  
    }  
}

具体区别

  • res.add(path);
    这行代码是将 path 这个列表的引用直接添加到 res 中。也就是说,res 中存储的是指向同一个 path 对象的引用。
    因此,后续如果对 path 进行修改(比如添加或删除元素),res 中对应的列表也会同步变化,因为它们指向的是同一个对象地址
  • res.add(new ArrayList<>(path));
    这行代码是创建了一个新的 ArrayList,并用 path 的当前元素初始化它,然后将这个新列表添加到 res 中。
    这样,res 中存储的是一个新的、独立的列表对象,与原来的 path 不共享地址。
    后续对 path 的修改不会影响到已经添加到 res 中的列表内容

为什么通常用 res.add(new ArrayList<>(path));

在回溯算法或者类似场景中,path 通常是一个动态变化的列表,随着递归的深入和回退不断添加和删除元素。如果直接用 res.add(path);,那么 res 中存储的所有列表都会指向同一个 path,最终所有结果都会变成相同的(即最后一次修改后的状态)。使用 new ArrayList<>(path) 可以保存当时的快照,保证结果的正确性

为什么剪枝操作先对数组进行排序
例如[2,3,5],target = 4;此时,2+2=4,后面的肯定大于4,可以剪枝

组合总和2

修改上面代码,超出时间限制,因为contains很慢!所以要在for循环中做剪枝操作,去重

class Solution {  
    List<List<Integer>> result = new ArrayList<>();  
    List<Integer> path = new ArrayList<>();  
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {  
        Arrays.sort(candidates);//剪枝  
        boolean[] used = new boolean[candidates.length];  
        backingtracking(candidates,target,0,0,used);  
        return result;  
    }  
    public void backingtracking(int[] candidates, int target,int startIndex,int sum,boolean[] used){  
        if(sum>target){  
            return;  
        }  
        if(sum == target){  
            //result.add(path);这里是错误的!  
            /*if(!result.contains(path)){                result.add(new ArrayList<>(path));            }*/            result.add(new ArrayList<>(path));  
            return;  
        }  
        for(int i = startIndex;i<candidates.length;i++){  
            if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false) continue;//相邻元素相同,只判断前一个元素,后面的会相同,实现了去重,这里的used数组只进行树层去重,没进行树枝去重,符合题意  
            if(sum+candidates[i]>target) break;//剪枝  
            sum+=candidates[i];  
            path.add(candidates[i]);  
            used[i] = true;  
            backingtracking(candidates,target,i+1,sum,used);  
            sum-=candidates[i];  
            path.remove(path.size()-1);  
            used[i] = false;  
        }  
    }  
}

这里注意是树枝去重还是数层去重!!!

分割回文串

不太懂

class Solution {  
    List<List<String>> result = new ArrayList<>();  
    List<String> path = new ArrayList<>();  
  
    public List<List<String>> partition(String s) {  
        backtracking(s, 0, new StringBuilder());  
        return result;  
    }  
  
    private void backtracking(String s, int start, StringBuilder sb) {  
        //因为是起始位置一个一个加的,所以结束时start一定等于s.length,因为进入backtracking时一定末尾也是回文,所以path是满足条件的  
        if (start == s.length()) {  
            result.add(new ArrayList<>(path));  
            return;  
        }  
        //像前两题一样从前往后搜索,如果发现回文,进入backtracking,起始位置后移一位,循环结束照例移除path的末位  
        for (int i = start; i < s.length(); i++) {  
            sb.append(s.charAt(i));  
            //sb.toString().equals(sb.reverse().toString())不用这种方法,因为reverse会改变sb的内容  
            if (check(sb)) {  
                path.add(sb.toString());  
                backtracking(s, i + 1, new StringBuilder());  
                path.remove(path.size() - 1);  
            }  
        }  
    }  
    private boolean check(StringBuilder sb){  
        for (int i = 0; i < sb.length()/ 2; i++){  
            if (sb.charAt(i) != sb.charAt(sb.length() - 1 - i)){return false;}  
        }  
        return true;  
    }  
}
posted @ 2025-04-18 11:20  Anson_502  阅读(13)  评论(0)    收藏  举报