给定一个不含重复数字的数组 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 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutations
思考
这题利用深度优先搜索一棵树。设置一个空路径path[]。然后寻找符合条件的path保存到res中。
每一个位置都有三种选择,利用约束条件,用过的数字不能再用了,来剔除不正确的路径。所以要用一个hashMap used{}记录选过的数,遇到就直接跳过。
第一个数字选择1的情况如下。
第一个数字选择2的情况如下。
整棵树的搜索如下。
橙色的线是pop,回溯。当找到一个正确的路径,或者dfs里面的for循环走完。需要把path的最后一位给pop出来。进行回溯。
递归函数
我们需要在这个树上,深度优先搜索DFS出所有符合条件的解。
DFS是根据当前的path继续选数,直到构建出合法的path,加入到res里面。
- 递归的入口,是一个空path,表示什么都没选。
- 函数体内,用for循环,枚举出所有可能的选项。再用if判断选项的合法性。
- 每一轮,做出一个选择,基于当前的path,继续选择。
- 递归的出口:当path长度和nuns长度相等,说明找到一条路径。或者for循环走完,dfs也会结束。之后要把path的最后一个数pop出来,同时状态设置为未使用。继续搜索。
代码实现
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums) {
const res = []
const used = {}
function dfs(path) {
if(path.length === nums.length) { //选够了
//res.push(path.slice())
//res.push(path.concat())
res.push([...path]) //这里是深拷贝,保存当前path。因为path一直在变
return;
}
//num前面要声明,不然for循环不起来
for(let num of nums) { //枚举可能的选择
if (used[num]) { //数字使用过了,跳过他。
continue
} //数字未使用过
path.push(num) //加入path
used[num]=true //改变使用状态
dfs(path) //搜索新的路径
path.pop() //递归结束,弹出最后一个数字
used[num] =false //复原使用状态
}
}
dfs([]); //递归入口,空path
return res;
};
知识
1.浅拷贝是拷贝引用,深拷贝是复制数组元素。因为path后面还在不停变,所以要把当前的path数组拷贝push进res里面。
2.深拷贝的方法
https://www.cnblogs.com/rysinal/p/7577664.html
slice方法
arr.slice(start,end)
方法返回一个新的数组,包含从 start 到 end (不包括该元素)的 arrayObject 中的元素。该方法并不会修改数组,而是返回一个子数组。
在这里我们的思路是直接从数组开头截到尾:
path.slice()
concat方法
concat() 方法用于连接两个或多个数组。
arrayObject.concat(arrayX,arrayX,......,arrayX)
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
使用这种方法的思路是我们用原数组去拼接一个空内容,放回的便是这个数组的拷贝:
path.concat()
解构赋值
将path解构返回一个新数组。
[...path]