“Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。”
一、题目描述
给定一个不含重复数字的数组 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] <= 10nums中的所有整数 互不相同
二、思路分析
这道题有点像我们高中时期的排列组合,给一个数组,然后将里面的元素随意打乱,看最终能有多少种不同的组合。那么要怎么列举出这些组合呢,我们肯定不会无规律地乱穷举,一般会这样想:先固定第一位,然后第二位是 2 ,第三位是 3 ;然后把第二位变成 3 ,第三位变成 2 ;然后把第一位换成 2 ,重复这种过程。
其实这就是回溯算法,本质上是一种暴力穷举算法。那么我们该怎么用代码实现这种算法呢?思考一下,我们是不是会:
- 将第一位这样变化
1或2或3 - 将第二位这样变化
2或3或1 - 将第三位这样变化
3或1或2那么首先for循环肯定是跑不掉的了,那么就是第一位进行for循环;在第一位的for循环里进行第二位的for循环,此时需要注意第二位不能与第一位相同,找到第一个不与第一位相同的元素后,就进行第三位的for循环,此时需要注意不能与前两位相同,然后找到第一个不与前两位相同的元素即可。
思考到这里了,已经有点眉目了。我们肯定不会这样写:
for () {
for () {
for () {}
}
}
聪明的你肯定已经想到递归调用自身,因此我们可以
function dfs(nums) {
for (let i = 0; i < nums.length; i++) {
dfs(nums);
}
}
有递归当然就有结束递归。由于最后返回的是数组,数组里的元素是排序组合后的小数组,因此可以全局定义一个数组 arr 用来存储最后返回的结果,定义一个 track 数组用来存储小数组,当小数组的长度为 nums.length 时,就将小数组压入 arr ,这就是结果递归的判断了。于是变成:
const arr = [], track = [];
function dfs (nums) {
if (track.length === nums.length) {
arr.push(Array.from(track));
return;
}
for (let i = 0; i < nums.length; i++) {
dfs(nums);
}
}
下面就是 for 循环里的数据处理了。在循环里要做什么呢?如果当前元素不与上一位相同就压入数组,如果相同就继续遍历。那如何判断元素是否相同呢?用 bool 数组判断,将当前元素压入栈后,bool 数组中下标为当前元素的值赋为 true ,也就是说循环时判断 bool 数组中下标为当前元素的值是否为 true。那么递归函数变成:
const arr = [], track = [];
function dfs (nums, used = []) {
if (track.length === nums.length) {
arr.push(Array.from(track));
return;
}
for (let i = 0; i < nums.length; i++) {
if (used[i]) continue;
track.push(nums[i]);
used[i] = true;
dfs(nums, used);
}
}
递归返回后,此时 track 数组已经满了,因此需要将元素取出来,并且每取一个出来就要将 used 数组里对应元素修改为 false 。因此:
const arr = [], track = [];
function dfs (nums, used = []) {
if (track.length === nums.length) {
arr.push(Array.from(track));
return;
}
for (let i = 0; i < nums.length; i++) {
if (used[i]) continue;
track.push(nums[i]);
used[i] = true;
dfs(nums, used);
track.pop();
used[i] = false;
}
}
至此,终于结束这道题了,最后一步如果还不理解为什么的话,这里贴下 debug 细节:
三、AC 代码
/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function(nums) {
const arr = [], track = [];
const dfs = function(nums, k, used) {
if (track.length === k) {
arr.push(Array.from(track));
return;
}
for (let i = 0; i < k; i++) {
if (used[i]) continue;
track.push(nums[i]);
used[i] = true;
dfs(nums, k, used);
track.pop();
used[i] = false;
}
}
dfs(nums, nums.length, []);
return arr;
};