LeetCode代码分析——47. Permutations II(全排列,DFS,visited数组剪枝)

题目描述

Given a collection of numbers that might contain duplicates, return all possible unique permutations.
给定一个数字的集合,集合中可能包含重复的数字,返回所有可能的唯一的全排列。

Example:

Input: [1,1,2]
Output:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

思路分析

47. Permutations的大体思路类似,就是采用DFS来求全排列。不同的是,这里的数字有可能重复,因此需要在DFS判断的基础上,在DFS树上进行剪枝操作。
传统DFS的套路是dfs(int[] nums, List temp, List> result),其中nums作为输入参数,temp作为DFS树每条路径的临时结果,result作为存放最终结果的输出参数。为了减少空间复杂度,作为临时结果的temp参数可以重用,通过在DFS执行方法后执行remove操作恢复现场
另外需要考虑的就是该如何剪枝?例如[1,1,2],如果按照不剪枝的方法执行DFS,得到的结果如图所示。

LeetCode代码分析——47. Permutations II(全排列,DFS,visited数组剪枝)_第1张图片
DFS树

如图的DFS数中,圈子划出的部分就是需要剪枝的部分,经过分析可以发现,需要剪枝的部分都是由于 对重复的元素进行了多余的递归操作。而需要判断重复的元素,就需要先对输入数组nums进行 排序,在DFS递归之前判断当前nums[i]是否和前面的nums[i-1]相等,并且通过visited[]数组判断DFS树是否是刚把nums[i-1]的处理完退出的情况:

  • 如果visited[i-1]==true,说明nums[i-1]没有退出,此时正处于如图所示的位置。
  • 如果visited[i-1]==false,说明DFS刚结束了左边的子树,打算以第二个1为基准进行多余的递归,所以开始剪掉。


    LeetCode代码分析——47. Permutations II(全排列,DFS,visited数组剪枝)_第2张图片
    visited[i-1]的状态判断当前处于DFS树的位置判断是否剪枝

代码实现

public class Solution {

    /**
     * 30 / 30 test cases passed.
     *  Status: Accepted
     *  Runtime: 9 ms
     *
     * @param nums
     * @return
     */
    public List> permuteUnique(int[] nums) {

        List> result = new ArrayList<>();

        if (nums == null || nums.length == 0) {
            return result;
        }

        boolean[] visited = new boolean[nums.length];
        List temp = new ArrayList<>();
        Arrays.sort(nums);
        permuteRecursively(nums, visited, temp, result);

        return result;
    }

    private void permuteRecursively(int[] nums, boolean[] visited, 
                              List temp, List> result) {

        if (temp.size() >= nums.length) {
            result.add(new ArrayList<>(temp));
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            // 前一层递归已经访问过,跳过
            if (visited[i]) {
                continue;
            }
            // i和前面的相同,但是前面的没有访问,
            // 只能是因为前面的已经退出来了,那这个也就跳过
            if (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]) {
                continue;
            }
            visited[i] = true;
            temp.add(nums[i]);
            permuteRecursively(nums, visited, temp, result);
            temp.remove(temp.size() - 1);
            visited[i] = false;
        }

    }

}

你可能感兴趣的:(LeetCode代码分析——47. Permutations II(全排列,DFS,visited数组剪枝))