LeetCode琅琊榜第十三层-全排列(DFS深度优先算法)

LeetCode.46全排列

难度:中等

博主空间往期力扣

题目链接 


目录

作者原始思路

API法

思想简述与代码分析

问题​

反省

官方解法-深度优先算法(DFS)

算法图解

示例:[1,2,3]全排列

​图解分析

算法思路

代码实现

代码分析

知识扩展

结论 


作者原始思路

API法

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        var newList = new ArrayList<Integer>();
        var set = new HashSet<List<Integer>>();
        for (int num : nums) {
            newList.add(num);
        }
        var n = nums.length;
        var sum = 1;
        while (n != 0) {
            sum *= n;
            n--;
        }
        while (set.size() < sum) {
            var l = new ArrayList<>(newList);
            Collections.shuffle(l);
            set.add(l);
        }
        return new ArrayList<>(set);
    }
}

思想简述与代码分析

  • 将所有的元素放于ArrayList集合中,利用Collections工具类里面的一个方法,可以将集合里面的元素打乱顺序
  • 将打乱的顺序放入新的List集合中,然后再加入Set集合,根据Set集合的无序不可重复性,就可以得到全排列的唯一解
  • 根据推导,N个元素一共有N!种情况,最后返回结果即可

问题

反省

  • 平常练习可以玩玩,到了考试或者是面试的时候可能就不给用API了,我们也不可能随时能看API,所以,这个方法仅供参考,不建议使用,所以,全排列的标准解法是什么呢?答案是DFS深度优先算法

官方解法-深度优先算法(DFS)

算法图解

示例:[1,2,3]全排列

图解分析

  • 全排列问题我们可以构建一颗多叉树,利用子节点去逐步增加数据长度
  • 假设刚开始的根节点是空集,则放置情况可以有三种,放1,放2或放3,所以对应图中一共有三个子节点
  • 根据以上思路,可以把多叉树创建出来
  • 我们发现,我们最终想要的结果都在叶子节点上,所以,我们得到结果的关键是构造一颗多叉树
  • 构建多叉树有两种方式,即DFS或BFS,这里采用了DFS

算法思路

  • 通过图解,我们可以知道什么时候取出结果,所以,我们需要有以下的状态变量
  • 1.当前深度depth:深度depth用于记录DFS现在处于第几层,如果是最后一层就得到结果
  • 2.路径path:路径path用于记录每一个节点的元素的情况,当然,path是动态的,因为DFS会有回溯过程,回溯后或递归中,与各节点的情况一致
  • 3.层数len:层数len用于判断是否到达了叶子节点那一层
  • 4.访问used:访问used用于记录元素是否被访问,避免同一个元素重复

代码实现

class Solution {
    public List<List<Integer>> permute(int[] nums) {
        var len = nums.length;
        var path = new ArrayDeque<Integer>();
        var used = new boolean[len];
        var res = new ArrayList<List<Integer>>();
        if (nums.length == 0) {
            return res;
        }
        dfs(nums,res,0,path,used,len);
        return res;
    }
        private void dfs(int[] nums, ArrayList<List<Integer>> res, int depth, ArrayDeque<Integer> path, boolean[] used, int len) {
        if (depth == len) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = 0; i < len; i++) {
            if (used[i]) {
                continue;
            }
            path.addLast(nums[i]);
            used[i] = true;
            dfs(nums,res,depth+1,path,used,len);
            path.removeLast();
            used[i] = false;
        }
    }
}

代码分析

  • 1.创建对应的变量
    • 1.1注意:path是一个栈,即用到了SUN公司所给的集合,便于我们最后的追加(在图例中,总是在后面追加)
  • 2.dfs方法的参数
    • 2.1nums数组,用于获取数据,追加到path中
    • 2.2res集合,用于将结果放入集合中
    • 2.3depth,path,used,len与思路一致,注意depth一开始为0,因为一开始是根节点
  • 3.如果当前的depth等于len,说明深度优先到了最后一层,就是我们所需要的答案,直接假如res集合中即可,直接return
  • 4.for循环
    • 4.1如果当前元素被访问,直接跳过
    • 4,2如果没有没访问,先将其追加到path后面,然后再设置为已访问
    • 4.3调用dfs方法,进行深度优先
  • 5.如果执行到这一步,说明已经发生了回溯,我们需要将之前追加和访问的元素删去,即设置为未访问,和把栈最后的元素删除

知识扩展

为什么要有for循环

  • 从图解可知,一层中有多个节点,多叉树中有多层,我们调用dfs方法就是为了从上一层跳到下一层,for循环的原因是遍历同一层的多个节点!

结论 

        深度优先算法,和动态规划算法一样,都是比较常见的算法,而且算法的难度也存在,所以,我们需要掌握深度优先这种算法,最后,我来总结一下的几点

        1.全排列问题如何构建多叉树

                2.如何从多叉树提取出关键的状态变量

                        3.深度优先算法的实现

评论 47
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爪哇土著、JOElib

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

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

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

打赏作者

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

抵扣说明:

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

余额充值