回溯算法之子集问题

文章目录

  • 1.前言
  • 2.子集问题
  • 3.子集中去重

1.前言

本文讲解回溯算法中的另一类问题,也就是子集问题。子集问题和组合问题类似,只是收集结果的时机不同。
如果对组合问题不太了解,可以看一下我的这两篇文章
回溯算法之组合和排列问题和回溯算法之组合和排列结果如何去重

2.子集问题

题目来自于https://leetcode.cn/problems/subsets/description/

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例2:

输入:nums = [0]
输出:[[],[0]]

问题分析,以示例1为例:
回溯算法之子集问题_第1张图片

其实这题和组合类似,不过与组合问题不同的是,组合问题是在叶子节点收集结果,而子集问题是在每一层都收集一次结果。

class Solution {
    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> path = new ArrayList<>();
    public List<List<Integer>> subsets(int[] nums) {
        backTracking(nums,0);
        return res;
    }

    public void backTracking(int[] nums,int startIndex){
        res.add(new ArrayList<>(path));
        if(startIndex >= nums.length){
            return ;
        }
        for(int i = startIndex;i < nums.length;i++){
            path.add(nums[i]);
            backTracking(nums,i+1);
            path.remove(path.size()-1);
        }
    }
}

这题的关键是在于res.add(new ArrayList<>(path));这行代码的位置,因为答案中有空集这个选项,因此将一行代码放到了第一行
终止条件:if(startIndex >= nums.length) 判断当前的起始索引是否超出了数组的长度,如果是,则返回,结束当前递归。

3.子集中去重

题目链接:https://leetcode.cn/problems/subsets-ii/submissions/604569551/
问题描述:

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的 子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例1:

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

示例2:

输入:nums = [0]
输出:[[],[0]]

与上一题不同,这题的输入中有重复元素,我们需要进行去重。去重的方法和组合一样,我们可以用used数据来标记哪些元素被使用到。

具体代码如下:

class Solution {
    public List<List<Integer>> res = new ArrayList<>();
    public List<Integer> path = new ArrayList<>();
    public boolean[] used ;
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        used = new boolean[nums.length]; 
        Arrays.sort(nums);
        backTracking(nums,0);
        return res;
    }

    public void backTracking(int[] nums,int startIndex){
        res.add(new ArrayList<>(path));
        if(startIndex >= nums.length){
            return;
        }
        for(int i = startIndex;i < nums.length;i++){
            if(i > 0 && nums[i] == nums[i-1] && !used[i-1]){
                continue;
            }
            path.add(nums[i]);
            used[i] = true;
            backTracking(nums,i+1);
            path.remove(path.size()-1);
            used[i] = false;
        }
    }
}
  • 使用 Arrays.sort(nums) 对数组进行排序,这样相同的元素会相邻排列,方便后续去重处理。
  • 去重逻辑:if(i > 0 && nums[i] == nums[i-1] && !used[i-1]) 这个条件用于避免生成重复的子集。当 i > 0 且当前元素和前一个元素相同,并且前一个元素没有被使用过(即 !used[i-1])时,跳过当前元素,继续下一次循环。

你可能感兴趣的:(刷题笔记,算法)