Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
一、题目描述:
返回数组所有可能的全排列。
给定一个数组,打印出数组的所有排列。比如数组为[1, 2, 3],那最终输出为:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
二、思路分析:
数组为 [1, 2, 3] 的全排列有 3! 种,也就是6种,从上面的结果中我们可以看出以1开头有两种,以2开头有两种,以3开头有两种,总共加起来是6种。注意因为一共就3个数字,不能重复选择(例如1,1)
所以我们分别选择,1开头,2开头,3开头
- 从1开头,我们可以选择 1,2 和 1,3 有两种可能
- 从2开头,我们可以选择 2,1 和 2,3 有两种可能
- 从3开头,我们可以选择 3,1 和 3,2 有两种可能
树形结构图如下:
当我们选择了两个元素之后,那么就剩下最后一个元素了,总共要选择三个元素。
当选择 1,2 时候那么我们只能选择 3,因为元素不能重复的选择,以此类推最后我们的结构如下图:
知道了规则我们就要开始写代码了,先看代码我再解释。
function backtrack (n, output, res, first) {
if (first === n) {
res.push(output.slice())
}
for (let i = first; i < n; i++) {
swap(output, first, i)
backtrack(n, output, res, first + 1)
swap(output, first, i)
}
}
function permute(nums) {
let n = nums.length
let res = []
backtrack(n, nums.slice(), res, 0)
return res
}
function swap (nums, a, b) {
let temp = nums[a]
nums[a] = nums[b]
nums[b] = temp
}
for循环里面调递归其实是很少见的情况。代码n表示数组的长度,数组为 [1, 2, 3] 会循环三次,每次循环都会调用递归。
但因为递归的特性,却大大增加了复杂度。我们来分析一下代码的执行:
当n=1时,代码执行5次
backtrack第1次调用 first=0 i=0
再次进入循环
backtrack第2次调用 first=1 i=1
再次进入循环
backtrack第3次调用 first=2 i=2
执行完成开始回退
first=1 i=1执行完成
backtrack第4次调用 first=1 i=2
再次进入循环
backtrack第5次调用 first=2 i=2
执行完成开始回退
first=1 i=2执行完成
first=1 i=1执行完成
整个过程复杂程度可见一斑,如果n再增加1,我想要非人才能分析清晰。