力扣第46/47题:全排列(回溯算法深度好题)

一、前言

        昨天说了,今天开始回溯算法的题目,今天看了两题非常经典的题目,感觉回溯的套路比动态规划更为明显。今天为什么说两题呢,因为其实47就是46的升级版而已,由无重复数字到有重复数字,我感觉一起讲更有助于理解。话不多说,看题

二、题目内容

 

三、题目分析

         首先来看第46题,一个无重复数字的数组求可能全排列,再一看示例内容,呵!这不是排列组合嘛!nums长为3,那答案就是A33=6,没错!确实是这样。但是如果让大家写出每一种情况,可能大家会这样写(假设nums=[1,2,3]):123,132,213,231,312,321,为什么是这个顺序呢,其实就跟树一样,第一次有三个选择,在每一次选择后,剩下的选择只有nums.length-1了。

        如图所示,那么这很明显是二叉树的深度遍历了。我们可以这样干,定义一个列表,然后从头开始遍历二叉树,只要列表的长度等于原数组长度,就给它保存下来,然后回到上个节点,再往另外一个子节点遍历,这样遍历一遍之后就可以保存完整的答案了。我们可以用列表类型的列表保存列表。 

public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> list=new ArrayList<List<Integer>> ();
        backtrack(list,new ArrayList<>(),nums);
        return list;
    }

        首先创建一个,Integer型列表的列表,然后进行回溯算法的方法,最后结束后返回,就可以得到答案了,仔细观察一下,backtrack中第二个参数很明显使我们用来保存每一次数据的,满足条件后将它加到list中。

        现在看看backtrack该怎么写,回溯算法跟递归一样,需要一个终止点。那么如我刚刚所说,一次回溯的截止点就是当暂时列表里的数据等于原数组的长度,这时候需要将templist加到list中去,并且return,也就是返回到上一层节点去。

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

        但是绝大多数的时候,上面的条件是不成立的,因为此时还没有加完,那么对于这部分,应该怎么处理呢,首先,应该把当前数据加入到templist中,之后递归调用backtrack。最后一步,也是回溯不同于递归的最重要原因,就是撤销,撤销就是将原来添加的数删除,为什么呢?因为list是引用传递,当遍历到叶子节点以后要往回走,往回走的时候必须把之前添加的值给移除了,否则会越加越多。

  tempList.remove(tempList.size() - 1);

        所以完整代码是

    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> list=new ArrayList<List<Integer>> ();
        backtrack(list,new ArrayList<>(),nums);
        return list;
    }
    public void backtrack(List<List<Integer>> list,List<Integer> templist,int []nums)
    {
        if(templist.size()==nums.length){
            list.add(new ArrayList<>(templist));
            return ;
        }
        for(int i=0;i<nums.length;i++)
        {
            if(templist.contains(nums[i]))
                continue;
            templist.add(nums[i]);
            backtrack(list,templist,nums);
            templist.remove(templist.size() - 1);
        }
    }

        至于如果数组中有重复数字,我们采取的方法是额外采用一个长度为nums.length的boolean数组,我们先将nums数组排序,相同的数肯定相邻,那在回溯时,判断的方法是:如果当前第i为数被使用过 ,就continue,如果当前数等于前一个数,并且前一个数没有被访问,那么也continue掉,那是因为同样的数留一个就够了。

        这样算的话,代码应该为:

  public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> list=new ArrayList<List<Integer>> ();
        Arrays.sort(nums);
        boolean []used=new boolean[nums.length];
        backtrack(list,used,new ArrayList<>(),nums);
        return list;
    }
    public void backtrack(List<List<Integer>> list,boolean []used,List<Integer> templist,int[]nums)
    {
        if(templist.size()==nums.length){
            list.add(new ArrayList<>(templist));
            return;
        }
        for(int i=0;i<nums.length;i++)
        {
            if(used[i])
                continue;
            if (i > 0 && nums[i - 1] == nums[i] && !used[i - 1])
                continue;
            used[i]=true;
            templist.add(nums[i]);
            backtrack(list,used,templist,nums);
            used[i]=false;
            templist.remove(templist.size()-1);
        }
    }

四、感言

        回溯算法比动态规划好玩多了

        明天再来

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
目描述: 给你两个版本号 version1 和 version2 ,请你比较它们。 版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由多位数字组成,可能包含前导零。每个版本号至少包含一个字符。修订号从左到右编号,下标从0开始,最左边的修订号下标为0 ,下一个修订号下标为1,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。 比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较忽略任何前导零后的整数值。也就是说,修订号1和修订号001相等。如果版本号没有指定某个下标处的修订号,则该修订号视为0。例如,版本1.0 小于版本1.1,因为它们下标为0的修订号相同,而下标为1的修订号分别为0和1,0 < 1。 返回规则如下: 如果 version1 > version2 返回 1, 如果 version1 < version2 返回 -1, 否则返回 0。 示例 1: 输入:version1 = "1.01", version2 = "1.001" 输出:0 解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1" 示例 2: 输入:version1 = "1.0", version2 = "1.0.0" 输出:0 解释:version1 没有指定下标为 2 的修订号,即视为 "0" 示例 3: 输入:version1 = "0.1", version2 = "1.1" 输出:-1 解释:version1 中下标为 0 的修订号是 0,version2 中下标为 0 的修订号是 1 。0 < 1,所以 version1 < version2 示例 4: 输入:version1 = "1.0.1", version2 = "1" 输出:1 示例 5: 输入:version1 = "7.5.2.4", version2 = "7.5.3" 输出:-1 提示: 1 <= version1.length, version2.length <= 500 version1 和 version2 仅包含数字和 '.' version1 和 version2 都是 有效版本号

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

少๑渊

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

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

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

打赏作者

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

抵扣说明:

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

余额充值