全排列和子集都是非常常见的DFS面试问题,因此把他俩放到一起来说,同时分析一下他们的不同,加强对回溯法和出递归条件的判断理解。

全排列II

全排列要考虑数组有重复的情况,因此我们要先对原数组进行一个排序(不排序也可以,但是需要牺牲空间,用一个set来保存结果)。

我们看原题:

输入一个字符串,打印出该字符串中字符的所有排列。

例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 

如果无重复,则题解为:

class Solution {
    public List<List<Integer>> permutation(int[] nums) {
        List<List<Integer>> resList = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return resList;
        }
        dfs(nums, resList, 0);
        return resList;
    }
    
    private void dfs(int[] nums, List<List<Integer>> resList, int index) {
        if (index == nums.length) {
            List<Integer> res = new ArrayList<>();
            for (int i = 0; i < nums.length; i++) {
                res.add(nums[i]);
            }
            resList.add(res);
            return;
        }
        
        for (int i = index; i < nums.length; i++) {
            swap(nums,i,index);
            dfs(nums, resList, index + 1);
            swap(nums,i,index);
        }
    }
    
    private void swap(int[] nums, int a, int b) {
        int tmp = nums[a];
        nums[a] = nums[b];
        nums[b] = tmp;
    }
}

如果有重复,则题解为:

提供一种不同的思路,不用swap做,而使用tmp数组保存结果。
class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(nums);
        int[] visited = new int[nums.length];
        backtrack(res, nums, visited, new ArrayList<Integer>());
        return res;

    }

    private void backtrack(List<List<Integer>> res, int[] nums, int[] visited, ArrayList<Integer> tmp) {
        if (tmp.size() == nums.length) {
            res.add(new ArrayList<>(tmp));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (visited[i] == 1 || (i > 0 && visited[i - 1] == 0 && nums[i - 1] == nums[i])) continue;
            visited[i] = 1;
            tmp.add(nums[i]);
            backtrack(res, nums, visited, tmp);
            tmp.remove(tmp.size() - 1);
            visited[i] = 0;
        }
    }
}

子集II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subsets-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        List<Integer> list = new ArrayList<>();
        Arrays.sort(nums);
        findAll(0,nums,list,res);
        return res;
    }

    public static void findAll(int index, int[] nums, List<Integer> list, List<List<Integer>> res) {
        res.add(new ArrayList<>(list));

        for (int i = index; i < nums.length; i++) {
            if (i != index && nums[i] == nums[i - 1]){
                continue;
            }
            list.add(nums[i]);
            findAll(i + 1, nums, list, res);
            list.remove(list.size() - 1);
        }
    }
}