【LeetCode】47. 全排列 II

47. 全排列 II

知识点:递归;回溯;排列

题目描述

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

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

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

解法一:回溯

回溯算法的模板:

result = []   //结果集
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)  //把已经做出的选择添加到结果集;
        return  //一般的回溯函数返回值都是空;

    for 选择 in 选择列表: //其实每个题的不同很大程度上体现在选择列表上,要注意这个列表的更新,
    //比如可能是搜索起点和重点,比如可能是已经达到某个条件,比如可能已经选过了不能再选;
        做选择  //把新的选择添加到路径里;路径.add(选择)
        backtrack(路径, 选择列表) //递归;
        撤销选择  //回溯的过程;路径.remove(选择)

核心就是for循环里的递归,在递归之前做选择,在递归之后撤销选择;


和46题的区别就在于本次含有重复数字,比如例1,我们开始做出的选择是112,121,这是以第一个1作为第一选择来的;接着我们如果拿第二个1做为第一选择,那仍然是112,121,重复了。所以需要去重,在回溯里只要涉及到去重肯定是要用排序的,然后比较当前元素和上一元素是否相同,而且上一元素的used是否为false。因为我们需要的是树层间的去重,而不是树枝上的去重,也就是是横向的,不是纵向的。 比如112,这个1是可以重复选的。

image

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        Stack<Integer> path = new Stack<>();
        boolean[] used = new boolean[nums.length];
        Arrays.sort(nums);
        backtrack(nums, res, path, used);
        return res;
    }
    private void backtrack(int[] nums, List<List<Integer>> res, Stack<Integer> path, boolean[] used){
        if(path.size() == nums.length){
            res.add(new ArrayList(path));
            return;
        }
        for(int i = 0; i < nums.length; i++){
            if(i > 0 && nums[i] == nums[i-1] && used[i-1] == false){
                continue;   //树层不能选一样的;
            }
            if(used[i]) continue;  //同一树枝上去重;
            //做选择;
            path.push(nums[i]);
            used[i] = true;
            //递归:开始下一轮选择;
            backtrack(nums, res, path, used);
            //撤销选择:回溯;
            path.pop();
            used[i] = false;
        }
    }
}
posted @ 2021-08-14 00:19  Curryxin  阅读(202)  评论(0编辑  收藏  举报
Live2D