LeetCode 46. 全排列 --javascript DFS

1,185 阅读2分钟

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]]

提示:

1 <= nums.length <= 6
-10 <= nums[i] <= 10

题解

回溯法

回溯法(backtracking)是优先搜索的一种特殊情况,又称为试探法,常用于需要记录节点状态的深度优先搜索。通常来说,排列、组合、选择类问题使用回溯法比较方便。 顾名思义,回溯法的核心是回溯。在搜索到某一节点的时候,如果我们发现目前的节点(及其子节点)并不是需求目标时,我们回退到原来的节点继续搜索,并且把在目前节点修改的状态 还原。

这样的好处是我们可以始终只对图的总状态进行修改,而非每次遍历时新建一个图来储存 状态。 在具体的写法上,它与普通的深度优先搜索一样,都有 [修改当前节点状态]→[递归子节 点] 的步骤,只是多了回溯的步骤,变成了 [修改当前节点状态]→[递归子节点]→[回改当前节点 状态]。 回溯法。有两个小诀窍,一是按引用传状态,二是所有的状态修 改在递归完成后回改。 回溯法修改一般有两种情况,一种是修改最后一位输出,比如排列组合;一种是修改访问标 记,比如矩阵里搜字符串。

分析

怎样输出所有的排列方式呢?对于每一个当前位置 i,我们可以将其于之后的任意位置交换, 然后继续处理位置 i+1,直到处理到最后一位。为了防止我们每此遍历时都要新建一个子数组储 存位置 i 之前已经交换好的数字,我们可以利用回溯法,只对原数组进行修改,在递归完成后再 修改回来。 我们以样例[1,2,3]为例,按照这种方法,我们输出的数组顺序为[[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]],可以看到所有的排列在这个算法中都被考虑到了。

coding

/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var permute = function(nums) {
   let ans = [];
   let start = 0, len = nums.length, _start = 0;
   backtracking(nums, start, ans);
   return ans;
};

const backtracking = function(_nums, level, ans) {
    if(level === _nums.length - 1) {
        ans.push([..._nums]);
        return;
    }
    for(let i = level; i < _nums.length; i++) {
        [_nums[i], _nums[level]] = [_nums[level], _nums[i]] //  修改当前节点状态
        backtracking(_nums, level + 1, ans); // 递归子节点
        [_nums[i], _nums[level]] = [_nums[level], _nums[i]] //  恢复当前节点状态
    }
}