• 回溯去重问题


    第一个题: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的数字

  • 相关阅读:
    Elasticsearch 安装踩坑!
    html实现飞机小游戏(源码)
    [思维]Longest Common Subsequence 2022牛客多校第8场 F
    转行学网络安全,月薪6k到30k,给兄弟们一些个人建议
    自然语言生成技术现状调查:核心任务、应用和评估(1)
    【昇腾310】【mindspore 安装后测试报错】ImportError: libacl_tdt_channel.so
    Vue教学19:Element UI组件深入探索,构建精致的Vue应用界面
    PDEBench-AI求解微分方程新基准
    代码随想录三刷day37
    电子电气架构 --- 关于DoIP的一些闲思 下
  • 原文地址:https://blog.csdn.net/m0_50043893/article/details/125410387