面试知识库

46. 全排列#

今天要拆解的这道题,是无数算法题的基础原型。很多同学第一次遇到时都会被绕晕,但只要理解了它的思维模式,你会发现自己打开了新世界的大门!

🧩 从密码锁说起#

小红最近买了一个新型密码锁,发现了一个有趣的规律:

  • 密码由3个不同数字组成
  • 每个数字都必须使用且只能用一次
  • 数字的顺序不同就算不同密码

这不就是典型的全排列问题吗?

问题描述#

题目目标#

给定一个不含重复数字的整数数组 nums ,返回其所有可能的全排列。你可以按任意顺序返回答案。

示例 1#

输入: nums = [1,2,3] 输出: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 说明: 把输入和输出放在一起看,会更容易直接抓住题目要求的变化。

💡 问题的本质#

LeetCode 46题”全排列”的描述是这样的:

给定一个不含重复数字的数组 nums
返回其所有可能的全排列

示例:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
plaintext

🤔 这题的关键是什么?#

本质上,我们在寻找:

  1. 所有可能的元素组合方式
  2. 通过递归回溯尝试所有选择路径
  3. 及时剪枝避免重复使用元素

这就是经典的回溯算法问题!

🎬 模拟运行:看看算法是如何工作的#

以nums=[1,2,3]为例,我们一步步拆解回溯过程:

全排列回溯树

Step 1: 选择第一个数字

  • 可选1/2/3
  • 假设选择1,当前路径[1]

Step 2: 选择第二个数字

  • 剩下可选2/3
  • 选择2,路径变为[1,2]

Step 3: 选择第三个数字

  • 只剩3,得到完整排列[1,2,3]
  • 回溯到上一步

Step 4: 撤销第二步的选择

  • 路径回到[1],重新选择3
  • 路径变为[1,3]

Step 5: 选择第三个数字

  • 只剩2,得到排列[1,3,2]
  • 继续回溯…

直到遍历所有可能的路径!

⚡ 代码实现:回溯解法#

🎯 算法要点解析#

回溯法的精髓在于:

  1. 路径选择:记录已经做出的选择
  2. 选择列表:当前可选的元素集合
  3. 终止条件:到达决策树的底层
  4. 状态重置:回到上一层前的清理操作

📊 复杂度分析#

时间复杂度:O(n×n!)

  • 排列总数是n!种
  • 每个排列需要O(n)时间复制到结果集

空间复杂度:O(n)

  • 递归栈深度最大为n
  • 使用布尔数组记录访问状态

🎯 面试官最爱追问#

Q:如果数组包含重复元素怎么办?
A:需要先排序,然后在回溯时跳过相同元素(LeetCode 47题)

Q:如何优化空间复杂度?
A:可以通过交换元素实现原地排列,不需要额外空间记录状态

Q:如何按字典序输出结果?
A:先对数组排序,回溯时按顺序选择元素

💡 举一反三#

这个模板还可以解决:

  • 组合总和(LeetCode 39)
  • 子集问题(LeetCode 78)
  • 电话号码字母组合(LeetCode 17)
  • N皇后问题(LeetCode 51)

🎁 思考题#

如果要求返回第k个排列,而不是所有排列,如何用O(n)的时间复杂度解决?

例如: 输入:n=3, k=3 输出:“213”

如果你知道答案,或者有自己的想法?欢迎在评论区留言讨论~

📝 代码模板总结#

回溯算法的通用步骤:

  1. 定义结果集和路径
  2. 遍历选择列表
  3. 做出选择并更新状态
  4. 递归进入下一层
  5. 撤销选择并恢复状态