47. 全排列 II

  1. 47. 全排列 II
  2. 题解
    1. 基本框架

47. 全排列 II

难度中等493收藏分享切换为英文接收动态反馈

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

示例:

输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

通过次数115,255

提交次数185,765

题解

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        boolean[] used = new boolean[nums.length];
        traceBack(nums, new LinkedList<Integer>(), used,res);
        return res;
    }
    void traceBack(int[] nums, LinkedList<Integer> temp, boolean[] used, List<List<Integer>> res){
        if( temp.size() == nums.length){
            res.add(new LinkedList<>(temp));
            return;
        }
        for(int i =0; i < nums.length; i++){
            //used[i] == true 被选择的过的数不用再放进去了
            //接下来,如果当前节点和他之前的节点一样, 并且他的前一个节点已经被遍历过了, 也不需要了
            if( used[i] == true || ( i > 0 && nums[i] == nums[i-1] && !used[i-1])){
                continue;
            }
            temp.add(nums[i]);
            used[i] = true;
            traceBack( nums, temp, used , res);
            temp.pollLast();
            used[i] = false;
        }
    }
}

加上(i>0 && nums[i]==nums[i-1] && !used[i-1]),注意:若used[i-1]==false表示在同一层前一个相同的数已被访问过然后又被撤销(撤销就是used[i]又被置为false)。实际上这里判断写!used[i-1]或者used[i-1]都可以,!used[i-1]是回溯树同一层剪枝(水平),used[i-1]是回溯数同一条剪枝(竖直),但是同一层剪枝效率更高。

基本框架

LinkedList result = new LinkedList();
public void backtrack(路径,选择列表){
    if(满足结束条件){
        result.add(结果);
    }
    for(选择:选择列表){
        做出选择;
        backtrack(路径,选择列表);
        撤销选择;
    }
}

全排列,没有重复

class Solution {
    //回溯算法:不合适就退回上一步

    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> list = new ArrayList<>(); //全局list用于存储最后的全排列结果
        int len = nums.length;

        //特判
        if(len==0)  return list;
        boolean[] used = new boolean[len]; //默认全为false,表示节点是否访问过(是:true,否:false)

        List<Integer> path = new ArrayList<>();//存储全排列路径
        dfs(nums,0,len,used,path,list);

        return list;

    }
    public void dfs(int[] nums, int depth, int len, boolean[] used,List<Integer> path, List<List<Integer>> list){ //depth用来表示当前的状态在第几层,当depth==len表示一条完整的排列已经找到了
        if(depth==len){
            list.add(new ArrayList<Integer>(path));
            return;
        }
        // 在非叶子结点处产生不同的分支,这一操作的语义是:在还未选择的数中依次选择一个元素作为下一个位置的元素,通过一个循环实现
        for(int i=0; i<len; i++){
            if(used[i]) continue;

            path.add(nums[i]);
            used[i] = true;

            dfs(nums,depth+1,len,used,path,list);
            // 注意:下面这两行代码发生「回溯」,回溯发生在从 深层结点 回到 浅层结点 的过程,代码在形式上和递归之前是对称的
            used[i] = false;
            path.remove(path.size()-1);

        }
    }
}

作者:cherry-n1
链接:https://leetcode-cn.com/problems/permutations-ii/solution/quan-pai-lie-i-quan-pai-lie-ii-hui-su-fa-javadai-m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 mym_74@163.com