package com.freetymekiyan.algorithms.level.medium; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Given a collection of integers that might contain duplicates, nums, return all possible subsets. * <p> * Note: The solution set must not contain duplicate subsets. * <p> * For example, * If nums = [1,2,2], a solution is: * <p> * | [ * | [2], * | [1], * | [1,2,2], * | [2,2], * | [1,2], * | [] * | ] * Company Tags: Facebook * Tags: Array, Backtracking */ public class Subsets2 { /** * Backtracking. * Sort the array first for skipping duplicates later. */ public List<List<Integer>> subsetsWithDup(int[] num) { if (null == num || num.length == 0) { return Collections.emptyList(); } List<List<Integer>> res = new ArrayList<>(); Arrays.sort(num); // Sort first. backtrack(res, num, 0, new ArrayList<>()); return res; } /** * Backtracking. DFS. * Traverse a each node of a solution tree. * For each number n in the nums array: * | If n is a duplicate of previous number, skip. * | Pick it and backtrack. * | Remove it. */ private void backtrack(List<List<Integer>> res, int[] nums, int pos, List<Integer> subsets) { res.add(new ArrayList<>(subsets)); // Dereference. for (int i = pos; i < nums.length; i++) { if (i != pos && nums[i] == nums[i - 1]) { // Check and skip duplicates. continue; } subsets.add(nums[i]); // Add. backtrack(res, nums, i + 1, subsets); // Backtrack. subsets.remove(subsets.size() - 1); // Reset. } } /** * Backtracking. * Duplicate with previous number will only be added if: * The previous number is already in subset. */ private void backtrackB(List<List<Integer>> res, int[] nums, int pos, List<Integer> subset) { if (pos == nums.length) { res.add(new ArrayList<>(subset)); // Dereference. return; } subset.add(nums[pos]); // Add. backtrack(res, nums, pos + 1, subset); // Backtrack. subset.remove(subset.size() - 1); // Reset. if (pos > 0 && nums[pos] == nums[pos - 1] && !subset.isEmpty() && nums[pos - 1] == subset .get(subset.size() - 1)) { return; } backtrack(res, nums, pos + 1, subset); } /** * DP. Bottom-up. * Build subsets level by level from empty set. * For each number n in nums: * | If n is not a duplicate: * | Insert n to each previous subsets and create new subsets. * | If n is a duplicate: * | Only need to insert n to the subsets that contains a previous duplicate. * E.g. [1 2 2] * [] => [], [1] => [], [1], [2], [1 2] => [], [1], [2], [1 2], [2 2], [1 2 2] * Add 2 to subsets which have 2, which is the latter half of result. */ public List<List<Integer>> subsetsWithDup2(int[] nums) { List<List<Integer>> res = new ArrayList<>(); res.add(new ArrayList<>()); // Empty set. if (null == nums || nums.length == 0) { return res; } Arrays.sort(nums); // Sort first. int j, prevSize = 0; for (int i = 0; i < nums.length; i++) { if (i != 0 && nums[i] == nums[i - 1]) { // Duplicate. j = prevSize; // # of previous sets before last number. } else { j = 0; // No dup, start from beginning. } prevSize = res.size(); // # of previous sets. // Add to previous sets with same num for (; j < prevSize; j++) { List<Integer> temp = new ArrayList<>(res.get(j)); temp.add(nums[i]); res.add(temp); } } return res; } }