题目

题目来源:力扣(LeetCode)

给定一个不含重复数字的数组 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]]

思路分析

以数组 [1, 2, 3] 的全排列为例。
先写以 1 开头的全排列,它们是:[1, 2, 3], [1, 3, 2],即 1 + [2, 3] 的全排列(注意:递归结构体现在这里);
再写以 2 开头的全排列,它们是:[2, 1, 3], [2, 3, 1],即 2 + [1, 3] 的全排列;
最后写以 3开头的全排列,它们是:[3, 1, 2], [3, 2, 1],即 3 + [1, 2] 的全排列。
总结搜索的方法:按顺序枚举每一位可能出现的情况,已经选择的数字在 当前 要选择的数字中不能出现。按照这种策略搜索就能够做到 不重不漏。这样的思路,可以用一个树形结构表示。

全排列 - 图1
image.png

  1. /**
  2. * @param {number[]} nums
  3. * @return {number[][]}
  4. */
  5. var permute = function(nums) {
  6. // 保存结果数组,保存每个路径(排列)
  7. const result = []
  8. // 调用回溯函数,传入参数
  9. backtracking(nums, nums.length, [], [])
  10. // 返回结果数组
  11. return result
  12. // 定义回溯递归函数,传入数组,长度,节点是否被使用过的数组
  13. // used 用来标记节点是否被用过 path 用来存储路径,定义为一个栈
  14. function backtracking(nums, len, used, path){
  15. // 递归出口
  16. // 如果到达叶子节点,将路径推入结果数组,并返回
  17. if(path.length === len) {
  18. result.push([...path])
  19. return
  20. }
  21. // 遍历候选字符
  22. for(let i = 0; i < len; i++){
  23. // 使用过就下一轮循环
  24. if(!used[i]){
  25. // undefind和fasle都会进来
  26. // 这里说明这个数还没有被使用,入栈path
  27. path.push(nums[i])
  28. // 标记这个数被使用过了
  29. used[i] = true
  30. // 开始进行递归
  31. backtracking(nums, len, used, path)
  32. // 回溯【状态重置】撤销之前的操作
  33. path.pop()
  34. used[i] = false
  35. }
  36. }
  37. }
  38. };