leetcode 46 全排列(47带重复元素)

当给定的数组没有重复元素时

 

方法一:运用临时的path路径,然后一个标记是否访问的visit[]数组,来进行DFS

注意java中的一个bug,一个List加到另外一个List里面,前者改变的时候,后者也会改变
 

方法二: 利用交换的思想,每次都固定前面排好的一部分,直接在给定的数组上面操作,从而省去visit数组和临时path路径

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        int len = nums.length;
        List<Integer> nnums = new ArrayList<>();
        for(int i:nums){
            nnums.add(i);
        }
        List<List<Integer>> res = new ArrayList<>();
        dfs(nnums,len,0,res);
        return res;
    }

    public void dfs(List<Integer> nums,int len,int index,
                    List<List<Integer>> res){
        if(index==len){
            res.add(new ArrayList(nums));
            return;
        }
        for(int i=index;i<len;i++){
            Collections.swap(nums,i,index);
            dfs(nums,len,index+1,res);
            Collections.swap(nums,i,index);
        }
    }
}

 

当给定的数组有重复元素时,需要进行剪枝(难点)

 

  • 在图中 ② 处,搜索的数也和上一次一样,但是上一次的 1 还在使用中;
  • 在图中 ① 处,搜索的数也和上一次一样,但是上一次的 1 刚刚被撤销,正是因为刚被撤销,下面的搜索中还会使用到,因此会产生重复,剪掉的就应该是这样的分支。
     

 
方法一思路图:

如何理解:

 

注意方法一需要排序

方法一:还是利用临时path路径和visit[]数组,加以判断

 
在代码中

  • 当一个点访问了时候,就continue
  • 当一个点满足上述的条件(与上一次搜索的数一样且上一个数没有在被使用)
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;

public class Solution {

    public List<List<Integer>> permuteUnique(int[] nums) {
        int len = nums.length;
        List<List<Integer>> res = new ArrayList<>();
        if (len == 0) {
            return res;
        }

        // 排序(升序或者降序都可以),排序是剪枝的前提
        Arrays.sort(nums);

        boolean[] used = new boolean[len];
        // 使用 Deque 是 Java 官方 Stack 类的建议
        Deque<Integer> path = new ArrayDeque<>(len);
        dfs(nums, len, 0, used, path, res);
        return res;
    }

    private void dfs(int[] nums, int len, int depth, boolean[] used, Deque<Integer> path, List<List<Integer>> res) {
        if (depth == len) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = 0; i < len; ++i) {
            if (used[i]) {
                continue;
            }

            // 剪枝条件:i > 0 是为了保证 nums[i - 1] 有意义
            // 写 !used[i - 1] 是因为 nums[i - 1] 在深度优先遍历的过程中刚刚被撤销选择
            if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
                continue;
            }

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

            dfs(nums, len, depth + 1, used, path, res);
            // 回溯部分的代码,和 dfs 之前的代码是对称的
            used[i] = false;
            path.removeLast();
        }
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = {1, 1, 2};
        List<List<Integer>> res = solution.permuteUnique(nums);
        System.out.println(res);
    }
}

作者:liweiwei1419
链接:https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

写 used[i - 1] 代码正确,但是不推荐的原因。https://leetcode-cn.com/problems/permutations-ii/solution/hui-su-suan-fa-python-dai-ma-java-dai-ma-by-liwe-2/
 
方法二:在原数组上动刀

//别人的解法
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> output = new LinkedList<>();
        ArrayList<Integer> nums_list = new ArrayList<>();
        for (int num:nums){
            nums_list.add(num);
        }
        int n = nums.length;
        backTrack(n, nums_list, output, 0);
        return output;
    }
    
    private void backTrack(int n, ArrayList<Integer> nums, List<List<Integer>> output, int first){
        if (first == n){
            output.add(new ArrayList<>(nums));
            return;
        }
        for (int i=first; i<n; i++){
            if (!isRepeat(nums, first, i)){
                Collections.swap(nums, first, i);
                backTrack(n, nums, output, first+1);
                Collections.swap(nums, first, i);
            }
        }
    }
    private boolean isRepeat(ArrayList<Integer> nums, int first, int n){
        int temp = nums.get(n);
        for (int i=first; i<n; i++){
            if (nums.get(i) == temp){
                return true;
            }
        }
        return false;
    }

作者:cllh1999
链接:https://leetcode-cn.com/problems/permutations-ii/solution/gai-jin-46guan-fang-ti-jie-qu-zhong-fu-by-cllh1999/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路:
哪一位可以与后面不相同的换(这个reapeat函数在剑指offer38中可用Hashset实现),就换,形成子路,换了之后就锁住

posted @ 2020-05-01 16:36  种树人  阅读(262)  评论(0编辑  收藏  举报