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

    private void dfs(int[] nums, boolean[] used, Deque<Integer> path, List<List<Integer>> 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();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值