612 字
3 分钟
全排列

题目描述#

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

解题思路#

  1. 回溯算法框架
    • 维护当前路径 path 和结果集 res
    • 遍历未使用元素,依次加入路径
    • 递归探索下一层决策
    • 回溯时移除最后选择的元素
  2. 使用状态标记
    • 使用 used 数组记录元素使用状态
    • 避免同一元素重复使用
  3. 终止条件
    • 路径长度等于原数组长度时保存结果
  4. 决策空间
    • 每层递归可选择的元素为未使用元素
    • 决策树深度等于数组长度

关键洞察#

  1. 决策树结构
    • 根节点为空路径
    • 每层扩展所有未使用元素
    • 叶子节点为完整排列
  2. 状态回溯机制
    • 递归返回时恢复 used 状态
    • 路径数组 pop() 撤销选择
  3. 无重复保证
    • 数组不含重复元素自然无重复排列
    • 若有重复需额外去重(本题无需)
  4. 路径独立性
    • 每个递归层级维护独立路径副本
    • 回溯时共享同一数组引用

复杂度分析#

指标说明
时间复杂度O(n × n!):存在 n! 种排列,生成每个排列需 O(n) 时间(递归树有 n! 叶子节点)
空间复杂度O(n):递归栈深度 O(n) + used 数组 O(n) + 路径数组 O(n)

:n 为输入数组长度,n! 为排列总数

方法时间复杂度空间复杂度(额外)结果顺序
回溯+标记O(n × n!)O(n)自然顺序
原地交换O(n × n!)O(1)非字典序

代码实现#

原地交换

/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function (nums) {
const result = [];
const dfs = (nums, x) => {
if (x === nums.length - 1) {
result.push(Array.from(nums));
return;
}
for (let i = x; i < nums.length; i++) {
[nums[i], nums[x]] = [nums[x], nums[i]];
dfs(nums, x + 1);
[nums[i], nums[x]] = [nums[x], nums[i]];
}
}
dfs(nums, 0);
return result;
};

回溯+标记

/**
* @param {number[]} nums
* @return {number[][]}
*/
var permute = function (nums) {
const result = [];
const dfs = (item, visited) => {
if (item.length === nums.length) {
result.push(item);
return;
}
for (let i = 0; i < nums.length; i++) {
if (visited[i] === 0) {
visited[i] = 1;
dfs([...item, nums[i]], visited);
visited[i] = 0;
}
}
}
const visited = new Array(nums.length).fill(0);
for (let i = 0; i < nums.length; i++) {
if (visited[i] === 0) {
visited[i] = 1;
dfs([nums[i]], visited);
visited[i] = 0;
}
}
return result;
}

相似题目#

全排列
https://website-truelovings-projects.vercel.app/posts/algorithms/全排列/
作者
欢迎来到StarSky的网站!
发布于
2025-08-12
许可协议
CC BY-NC-SA 4.0