数组全排列问题

170 阅读2分钟

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 有两种可能

树形结构图如下:

截屏2022-03-17 上午9.43.10.png

当我们选择了两个元素之后,那么就剩下最后一个元素了,总共要选择三个元素。

当选择 1,2 时候那么我们只能选择 3,因为元素不能重复的选择,以此类推最后我们的结构如下图:

截屏2022-03-17 上午9.50.09.png

知道了规则我们就要开始写代码了,先看代码我再解释。

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,我想要非人才能分析清晰。