47. 全排列 II

245 阅读1分钟

题目介绍

力扣47题:leetcode-cn.com/problems/pe…

image.png

回溯

该题目跟[46. 全排列]类似,只不过数组nums中含有重复元素,这时候需要考虑去重的问题。

重复即为:存在相同数字,比如 [1,2,2'],那么答案 [1,2,2'][1,2',2] 就其实是一样的,在保存结果的时候,我们只选择其一,但是这不是字符串,在保存结果的时候再去判断是否答案里已经保存了这一种情况会比较麻烦,那么我们能不能在生成答案的过程中就将其 剪枝(类比用过的数字就不考虑),这样根本就不会生成重复的答案了。

在考虑重复及剪枝条件的时候,我们考虑的是:当前元素和前一个元素值相同(此处隐含这个元素的index > 0),并且前一个元素还没有被使用过的时候,我们要剪枝

if(i > 0  && nums[i-1] == nums[i] && check[i-1] == 0) {
    continue;
}

如下图所示,灰色为剪枝部分,蓝色为答案部分

image.png

代码如下:

class Solution {
    private List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        if(nums.length == 0) {
            return res;
        }
        //首先进行排序,方便后面进行去重
        Arrays.sort(nums);
        List<Integer> path = new ArrayList<>();
        //该数组用于记录哪些元素已经被访问过
        int[] check = new int[nums.length];
        backTrace(nums, check, path);
        return res;
    }

    public void backTrace(int[] nums, int[] check, List<Integer> path) {
        //满足结束条件的路径
        if(path.size() == nums.length) {
            res.add(new ArrayList<>(path));
            return;
        }
        for(int i = 0; i < nums.length; i++) {
            //当前元素如果已经被访问过的话,则直接跳过
            if(check[i] == 1) {
                continue;
            }
            //如果前一个相同元素没有被访问过,则直接跳过,用于去重
            if(i > 0  && nums[i-1] == nums[i] && check[i-1] == 0) {
                continue;
            }
            //做选择
            path.add(nums[i]);
            check[i] = 1;
            //递归
            backTrace(nums, check, path);
            //撤销选择
            path.remove(path.size() -1);
            check[i] = 0;
        }
    }
}