18.带重复元素的子集

描述

给定一个可能具有重复数字的列表,返回其所有可能的子集

注意事项

子集中的每个元素都是非降序的
两个子集间的顺序是无关紧要的
解集中不能包含重复子集

样例

如果 S = [1,2,2],一个可能的答案为

[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

挑战

你可以同时用递归与非递归的方式解决么?

代码

class Solution {
    /**
     * @param nums: A set of numbers.
     * @return: A list of lists. All valid subsets.
     */
    public ArrayList> subsetsWithDup(int[] nums) {
        ArrayList> results = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return results;
        }

        Arrays.sort(nums);

        ArrayList subset = new ArrayList();
        subsetWithDupHelper(nums, 0, subset, results);

        return results;
    }

    private void subsetWithDupHelper(int[] nums,
                                     int startIndex,
                                     ArrayList subset,
                                     ArrayList> results) {
        results.add(new ArrayList(subset));

        for (int i = startIndex; i < nums.length; i++) {
            /* 本来初值是令i = startIndex,正常运行代码不应该出现取第二个不取
             * 第一个的情况,但此条if本身就是用于表示异常的,所以一切就变得
             * 可以理解了
             */

            if (i != 0 && nums[i] == nums[i - 1] && i > startIndex){
            /* i != 0是为了防止 i - 1越界,
             * nums[i] = nums[i - 1] 即当前数等于前一个数,
             * 从下面 subset 两行代码可以看出如果 nums[i - 1] 被加入 subset,
             * 那么下一个startIndex = i - 1 + 1 = i;
             * i > startIndex等价于i >= startIndex + 1,而上一个数是startIndex - 1;
             * 说明在startIndex - 1和startIndex + 1间存在一个数,
             * 存在当前值nums的前一个与它相等的值并没有被添加到list里,这样会重复
             */
                continue;
            }
            
            subset.add(nums[i]);
            subsetWithDupHelper(nums, i + 1, subset, results);
            subset.remove(subset.size() - 1);
        }
    }
}

本题和subset相比就多了一个去重的过程,去重的逻辑已经在注释里说清楚了,稍微补充点就是比如数组是[1,2,2],第一个2用A表示,第二个2用B表示,我们要的顺序是[1,A],此时的要去的重就是[1,B]这种组合,比较容易混淆的是[1,A,B]这种情况A,B两个2在同一个list里并不算重复,去除的是两个list之间的重复情况

你可能感兴趣的:(18.带重复元素的子集)