LeetCode-047-全排列II

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations-ii

解题思路

借助深度遍历 dfs 的思想, 结合条件判断进行"剪枝"提高效率
dfs(nums, used, path, result)
nums 为数组
used 为标记数组, 用于判断该元素是否使用过
path 是深度遍历过程的中间变量, 保存了当前遍历的路径
result 是结果集
注意!剪枝的前提是 nums 已排序
dfs 递归时先判断 path 的长度是否等于 nums 的长度, 等于则说明深度遍历到最底部了, 添加结果
否则遍历数组

  • 如果当前数已被使用, 跳过

  • 如果当前数和前一个数相等, 并且前一个数"未使用", 跳过

    剪枝的一步, 剪枝就是要避免深度遍历的同一层出现相同元素, 但是允许不同层出现相同元素
    如果前一个数"未使用", 则肯定是同一层已经遍历过然后在回溯的时候设置为"未使用", 所以要跳过

  • 否则将当前元素标记为"使用过"并添加到 path 然后递归 dfs

  • 递归返回时要注意重置, 当前元素标记为"未使用"并从 path 从删除

代码

class Solution {
    public List> permuteUnique(int[] nums) {
        List> result = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return result;
        }
        // 排序是剪枝的前提
        Arrays.sort(nums);
        // 中间变量, 深度遍历过程中的路径
        Deque path = new LinkedList<>();
        // 标记元素是否被使用过
        boolean[] used = new boolean[nums.length];
        dfs(nums, used, path, result);
        return result;
    }

    private void dfs(int[] nums, boolean[] used, Deque path, List> result) {
        // 如果中间变量path长度等于数组长度, 添加结果
        if (path.size() == nums.length) {
            result.add(new LinkedList<>(path));
        } else {
            for (int i = 0; i < nums.length; i++) {
                // 避免深度遍历的同一层出现一样的数字
                // 当前数字和前一个数字相同时
                // 如果前一个数字"未使用", 则说明同一层已经使用过这个数字
                // 如果前一个数字"已使用", 则说明是不同层
                if (used[i] || (i > 0 && nums[i] == nums[i - 1] && !used[i - 1])) {
                    continue;
                }
                used[i] = true;
                path.addLast(nums[i]);
                dfs(nums, used, path, result);
                used[i] = false;
                path.removeLast();
            }
        }
    }
}

你可能感兴趣的:(LeetCode-047-全排列II)