LeetCode 46. 全排列

70 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第40天,点击查看活动详情.

46. 全排列

题目描述

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

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

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

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

解题思路

思路一: 回溯

我们定义递归函数 dfs(nums, len, depth, path, used, res) 表示从左往右填到第 depth 个位置,当前排列为 path。 那么整个递归函数分为两个情况:

  • 如果 depth === nums.length,说明我们已经填完了 nums.length 个位置(注意下标从 0 开始),找到了一个可行的解,我们将 path 放入答案数组中,递归结束。

  • 如果 depth < nums.length,我们要考虑这第 depth 个位置我们要填哪个数。根据题目要求我们肯定不能填已经填过的数,因此很容易想到的一个处理手段是我们定义一个标记 used 来标记已经填过的数,那么在填第 depth 个数的时候我们遍历题目给定的 length 个数,如果这个数没有被标记过,我们就尝试填入,并将其标记,继续尝试填下一个位置,即调用函数 dfs(nums, len, depth + 1, path, used, res)。回溯的时候要撤销这一个位置填的数以及标记,并继续尝试其他没被标记过的数

实现代码如下:

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permute = function(nums) {
    let len = nums.length,
        res = [],
        // 判断数字是否被选择过
        used = {};

    dfs(nums, len, 0, [], used, res);
    return res;
};



function dfs(nums, len, depth, path, used, res) {
    if (depth == len) {
        res.push([...path]);
        return;
    }

    for (let i = 0; i < len; i++) {
        if (!used[i]) {
            path.push(nums[i]);
            used[i] = true;
            // 递归 
            dfs(nums, len, depth + 1, path, used, res);

            used[i] = false;
            path.pop(); 
        }
    }
} 
  • 时间复杂度: O(n×n!)O(n×n!), n 为序列的长度
    dfs调用次数分析:
    在第 1 层,结点个数为 n 个数选 1 个的排列,时间复杂度为 An1A_n^1; 在第 2 层,结点个数为 n 个数选 1 个的排列,时间复杂度为 An2A_n^2;
    ...
    故有: 1+An1+An2+...+Ann1=1+n!(n1)!+n!(n2)!+...+n!n!(1(n1)!)+1(n2)!)+...+12n!1 + A_n^1 + A_n^2 + ... + A_n^{n-1} = 1 + \frac{n!}{(n-1)!} + \frac{n!}{(n-2)!} + ... + n! \le n!(\frac{1}{(n-1)!}) + \frac{1}{(n-2)!}) + ... + 1 \le 2n!
    总时间复杂度为: O(n×n!)O(n×n!)

  • 空间复杂度: O(n)O(n), 需要额外的空间且该空间取决于递归的深度

小结:
回溯框架: for循环中递归回溯,回溯前选择元素push到路径,回溯结束从路径中pop掉
由于全排列是没有顺序的,因此不设start参数,也正因此,回溯前需判断路径中是否已经存在,存在的话
则continue继续for,否则选择->递归回溯->撤销选择

参考资料

回溯算法入门级详解 + 练习(持续更新) - 全排列 - 力扣(LeetCode)

全排列 - 全排列 - 力扣(LeetCode)