Leetcode: 全排列

47 阅读3分钟

原题:leetcode.cn/problems/pe…

题目描述

给定一个不含重复数字的数组 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)非字典序

代码实现

  • JavaScript(原地交换)
/**
 * @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;
};
  • python
def permute(nums: list) -> list[list]:
    def backtrack(path, choices):
    if len(path) == len(nums):
        result.append(path[:])
        return

    for i in range(len(choices)):
        backtrack(path + [choices[i]], choices[:i] + choices[i+1:])

    result = []
    backtrack([], nums)
    return result
  • rust(回溯+标记)
pub fn permute(nums: Vec<i32>) -> Vec<Vec<i32>> {
    fn backtrack(path: &mut Vec<i32>, choices: &[i32], result: &mut Vec<Vec<i32>>) {
        if path.len() == choices.len() {
            result.push(path.clone());
            return;
        }
        for i in 0..choices.len() {
            if path.contains(&choices[i]) { continue; }
            path.push(choices[i]);
            backtrack(path, choices, result);
            path.pop();
        }
    }

    let mut result = vec![];
    backtrack(&mut vec![], &nums, &mut result);
    result
}

实际应用场景

  1. 密码破解:字符排列组合生成字典`
  2. 数据测试:多参数组合测试用例生成`
  3. 游戏设计:棋盘布局生成`
  4. 路由规划:旅行商问题暴力求解基础`

相似题目