深搜&回溯&剪枝优化策略-全排列II

LCR 084. 全排列 II - 力扣(LeetCode)

这道题的主体思想和之前讲过的全排列是相似的,不同的是思考的角度要侧重于剪枝方向,所以可以通过这道题对剪枝思想的进一步扩展;

通过题意,可以知道,在上一层全排列的基础上,增加了重复元素的条件,重复元素的添加,可能会导致重复子序列的出现,例如如下情况:

要避免这种情况,就是要在根源上去剪枝:相同节点的所有分支中,同一个元素只能选择一个,类比于上图,就是在节点 [1,1,2] 中,有两个1,那么只能选择一个。
(还要加上之前的剪枝条件:同一个数只能使用一次)

画决策树,考虑代码实现: 

首先要对数组进行优化,让他实现从小到大排序,让重复的元素排列在一起,方便后续的分析; 

要让同一节点下,相同的元素只能选择一次,结合下图分析:

所以做出剪枝代码: 

代码实现:

class Solution {
    List<List<Integer>> ret;
    List<Integer> str;
    boolean[] check;
    public List<List<Integer>> permuteUnique(int[] nums) {
        ret = new ArrayList<>();
        str = new ArrayList<>();
        check = new boolean[nums.length];
        Arrays.sort(nums);           // 排序
        dfs(nums);
        return ret;
    }
    public void dfs(int[] nums){
        if(str.size() == nums.length){
            // 此处使用 new ArrayList(str) 而不是直接使用 str,是因为
            // 如果直接add str,那么后续对这个全局变量进行修改,都会导致之前存储的值都进行改变
            // 所以此处就采用创建一个新的对象来避免这个问题
            ret.add(new ArrayList(str));    // 递归出口
        }

        for(int i=0;i<nums.length;i++){
            // 剪枝
            if(check[i] == true || (i!=0 && nums[i]==nums[i-1] && check[i-1]==false)){
                continue;
            }

            str.add(nums[i]);
            check[i] = true;

            dfs(nums);

            // 回溯
            check[i] = false;
            str.remove(str.size()-1);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PlLI-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值