欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

题目描述

  • 难度:中等

  • 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案

  • 示例 1

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
  • 示例 2
输入:nums = [0,1]
输出:[[0,1],[1,0]]
  • 示例 3
输入:nums = [1]
输出:[[1]]

个人回溯和46题的理解

  • 在很多刷题文章和书籍中,此题都被用做回溯算法的第一题,可见该题很有代表性,搞定此题意味着对回溯有了最基本的了解,当然就个人感受而言,以此作为回溯的第一题弊端也不小:本以为掌握了基本套路,刷其他回溯题的时候套上去即可,结果后来发下一道题都套不成功...

  • 套不成功?是因为此题没有代表性吗?当然不是,这是道典型的回溯算法题,但个人的感觉是:解题的关键不是套用模板,而是对回溯思想的理解,我个人的理解是:深度至上

  • 所谓深度至上,就是弄清楚在当前深度能做什么,例如46题全排列,一个深度意味着可选数字中做了一轮选择,每选中一个,都牢牢占据这一层的固定位置,下面的子树都要有他

  • 只要理解了深度至上,就清楚在当前做任何事情的时候都要确保深度固定,下图是[1,2]两个数字全排列的手绘图,边上数字表示选择,方框中的数字表示选择后的结果,请看蓝色框,在选择2的时候,要牢记当深度只能有一个数字,于是,刚才选择1的时候记录存在路径中的1就要果断删除,牢记当前层应该占据哪个位置,回溯的效果就有了

解题思路

  • 要用回溯算法解此题,有几个关键要注意

  • 全排列,意味着相同数字只要排列不同,也能算作结果的一种

  • 虽然不推荐用模板去套,但回溯该有的几个核心概念还是不能少的:

  1. 终止条件:只要组合的数字达到给定数字的长度,就可以终止了

  2. 路径:就是正在组合的元素,可以用数组表达

  • 此外还要有个辅助参数,用于记录那些值不能参与组合,以上图为例,在蓝色那一层如果选择了1,那么在下一层就不能再选择1了,所以在组合的时候,要有地方可以查到1不可用

编码

  • 接下来可以写代码了,如下,有几处要注意的地方稍后会提到
public class L0046 {

    List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> permute(int[] nums) {
// 回溯过程中的重要辅助参数:标记nums数组中有哪些已经使用过
boolean[] used = new boolean[nums.length];
// 回溯过程中的重要辅助参数:路径
int[] path = new int[nums.length]; dfs(nums, used, path, 0);
return res;
} public void dfs(int[] nums, boolean[] used, int[] path, int depth) {
// 终止条件(深度达到)
// 搜集:栈入res
// 本题的终止条件是一次组合的长度达到数组长度
if (depth==nums.length) {
// 搜集结果
// 千万注意:这个path一直在使用中,所以不能把path放入res中,而是path的元素
// res.add(Arrays.stream(path).boxed().collect(Collectors.toList())); List<Integer> list = new ArrayList<>();
for(int val : path) {
list.add(val);
} res.add(list);
return;
} // for循环
for (int i=0;i<nums.length;i++) {
// 如果当前数字没有用到,就标记,进入path,再做dfs
if (!used[i]) {
used[i] = true;
// 注意,path的下标千万不要用i!
// 例如1和2的全排列,在制造[2,1]的时候,i=1,但此时要修改的是path[i],
// 所以path的下标应该是depth
path[depth] = nums[i];
// 一次递归,深度要加一
dfs(nums, used, path, depth+1);
used[i] = false;
}
}
} public static void main(String[] args) {
List<List<Integer>> list = new L0046().permute(new int[] {1,2,3}); list.forEach(one -> {
Tools.printOneLine(one);
}); }
}
  • 上述代码有以下几处要注意
  1. res用于搜集达到终止条件的记录,也就是数字组合结果
  2. dfs方法就是本次回溯操作的核心方法
  3. 下图红色箭头所指代码就是本题最重要的一行,可见for循环的执行过程中,修改的都是path数组的同一个位置的值,这就是刚才提到的深度至上,只有进入了下面的dfs方法后,深度变化,修改的path数组的位置才会发生变化

  1. used数组用来记录深度调用过程中,那些数字已经被使用了,当前不要再使用
  2. 很多回溯的代码中,用栈对象保持path中的数据,入栈push,出栈pop都是标准操作,但是本题中用char数组,再配合depth,就可以满足需要了,这种原始类型的使用也会带来更好的性能

执行结果

  • 写完代码提交,执行结果如下,超过100.00%的提交

  • 至此,回溯入门实战已经完成,此时需要强烈提示:代码中那个for循环,在每一层都遍历nums的所有元素,那是此题的特殊操作,千万不要以为是模板或者套路,其他回溯题中,不会像此题这样每一层都遍历的

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

LeetCode46全排列(回溯入门)的更多相关文章

  1. [LeetCode] 46. Int数组全排列 ☆☆☆(回溯)

    描述 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3]输出:[ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2, ...

  2. [LeetCode] 46. 全排列(回溯)

    ###题目 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], ...

  3. [Swift]LeetCode46. 全排列 | Permutations

    Given a collection of distinct integers, return all possible permutations. Example: Input: [1,2,3] O ...

  4. [LeetCode] 784. 字母大小写全排列 ☆☆☆(回溯、深度优先遍历)

    https://leetcode-cn.com/problems/letter-case-permutation/solution/shen-du-you-xian-bian-li-hui-su-su ...

  5. Permutations 全排列 回溯

    Given a collection of numbers, return all possible permutations. For example,[1,2,3] have the follow ...

  6. LeetCode通关:连刷十四题,回溯算法完全攻略

    刷题路线:https://github.com/youngyangyang04/leetcode-master 大家好,我是被算法题虐到泪流满面的老三,只能靠发发文章给自己打气! 这一节,我们来看看回 ...

  7. [LeetCode] 17. 电话号码的字母组合 ☆☆☆(回溯) ###

    描述 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 示例: 输入:"23"输出:[&q ...

  8. LeetCode 47. 全排列 II(Permutations II)

    题目描述 给定一个可包含重复数字的序列,返回所有不重复的全排列. 示例: 输入: [1,1,2] 输出: [ [1,1,2], [1,2,1], [2,1,1] ] 解题思路 类似于LeetCode4 ...

  9. uva140

    全排列回溯剪枝. 题目数据很水.记录当前最小带宽,边回溯边计算当前序列最大的距离(也就是带宽),如果当前带宽超过了当前的最小带宽就剪枝. 注意下,数据读入时的字符串处理. AC代码 #include& ...

  10. (剑指Offer)面试题43:n个骰子的点数

    题目: 把n个骰子仍在地上,所有骰子朝上一面的点数之和为s.输入n,打印出s的所有可能的值出现的概率. 思路: s可能出现的值的范围为:n--6*n 1.全排列 回溯法枚举n个骰子(6面)的全排列,然 ...

随机推荐

  1. vue iview 单击table行变色 获取行数据

    用到两个事件: 代码如下: 1 <Table :columns="columns" :data="tableData" @on-row-click=&qu ...

  2. SpringBoot打包成WAR包的时候把第三方jar包打到LIB文件夹下和把第三方jar包打入到SpringBoot jar包中

    SpringBoot打包成WAR包的时候把第三方jar包打到LIB文件夹下和把第三方jar包打入到SpringBoot jar包中 转载 首先我们应该知道我们把SPRINGBOOT项目打包成WAR包和 ...

  3. DASCTF二进制专项部分Writeup

    easynote create:堆大小可以任意分配只要不超过0xFFF create()  unsigned __int64 create() { int i; // [rsp+0h] [rbp-20 ...

  4. 关于int**在malloc为二维数组分配空间时候的作用见解

    关于int**在用malloc函数为二维数组分配空间时候 int**   二级指针类型 二维数组的数组名为行指针,写成  arr  =(char**)malloc(n*sizeof(char))时,a ...

  5. 【Python&GIS】矢量数据投影转换(WGS84转地方坐标系)

         又是掉头发的一天,今天的任务是将WGS84坐标系的点转成地方坐标系,并判断点是否在某个面内,找了半天的资料什么四参数.七参数啥的太复杂了.这里使用Python的ogr, osr库内置的坐标转 ...

  6. 项目小结:使用Docker迁移服务到离线服务器

    前言 最近遇到的这个场景,需要把之前开发的一套系统迁移到一个离线的服务器上,这个服务器有点麻烦,接入VPN后通过堡垒机才能访问,速度也很慢,遇到不少坑,本文记录一下迁移过程. 基本信息 原本这套系统也 ...

  7. 2023-06-27:redis中什么是缓存雪崩?该如何解决?

    2023-06-27:redis中什么是缓存雪崩?该如何解决? 答案2023-06-27: 缓存雪崩是指当缓存层承载大量请求并有效保护存储层时,如果缓存层由于某些原因无法提供服务,例如缓存数据大面积失 ...

  8. .NET 5 的烦恼

    由于微软工程师的辛勤脑洞和劳作,.NET 生态如何演变完全看他们的决策,其中包含社区吸纳内容.团队讨论结果等等,不乏一些工程师.架构师偏好,很难摸得准.   就比如这一次未来规划,他们希望将 .NET ...

  9. CentOS 7相关操作

    防火墙操作 开启防火墙 sudo systemctl start firewalld.service 查看防火墙状态 sudo systemctl status firewalld.service 关 ...

  10. 一文帮你搞定H5、小程序、Taro长列表曝光埋点

    对于很多前端同学来说,"埋点"常常是一个不愿面对却又无法逃避的话题.为什么这么说呢,相信很多前端同学都深有体会:首先埋点这个事基本是前端"独享"的,服务端基本不 ...