【面试-leetcode46】全排列(递归回溯+交换法)

255 阅读2分钟

freysteinn-g-jonsson-s94zCnADcUs-unsplash.jpg

这是leetcode面试刷题一题多解系列的第6篇,给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。练习递归回溯法和交换法。

题目

今天跟大家一起看一道 全排列 经典的回溯算法问题,分别了解递归回溯法和交换法两种题解思路。

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

来源:力扣(LeetCode)
链接:leetcode.cn/problems/pe…
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解1---递归回溯法

递归回溯法是一种常用于解决排列组合问题的算法。其基本思想是通过递归的方式,不断地将问题划分为子问题,并在每一步选择一个未被选取的数字作为当前位置的值,直到所有位置都被填满,得到一个完整的排列。

var permute = function(nums) {
    const result = [];
    const backtrack = (start) => {
        if (start === nums.length) {
            result.push(nums.slice()); // 将当前排列加入结果数组
            return;
        }
        for (let i = start; i < nums.length; i++) {
            [nums[start], nums[i]] = [nums[i], nums[start]]; // 交换当前位置与后面位置的值
            backtrack(start + 1); // 继续递归填充下一个位置
            [nums[start], nums[i]] = [nums[i], nums[start]]; // 恢复当前位置的值
        }
    }
    backtrack(0); // 从第一个位置开始递归回溯
    return result;
};

上述代码中,定义了一个 backtrack 函数作为递归的核心实现。它从 start 位置开始,将当前位置与后面位置的值交换,并继续递归填充下一个位置,直到所有位置都被填满。每当得到一个完整的排列时,将其加入结果数组 result 中。

递归回溯法的时间复杂度为 O(n!),空间复杂度为 O(n),其中 n 是数组的长度。因为全排列问题的解个数为 n!,每次递归调用会产生一个新的排列,因此时间复杂度为阶乘级别。

题解2---交换法

交换法是另一种常用于解决全排列问题的方法。其基本思想是通过不断地交换数组中的元素,使得数组逐渐变成目标排列,从而得到所有的排列组合。

var permute = function (nums) {
    const result = [];
    const swap = (i, j) => {
        [nums[i], nums[j]] = [nums[j], nums[i]]; // 交换数组中的两个元素
    }
    const
        backtrack = (start) => {
            if (start === nums.length) {
                result.push(nums.slice()); // 将当前排列加入结果数组
                return;
            }
            for (let i = start; i < nums.length; i++) {
                swap(start, i); // 交换当前位置与后面位置的值
                backtrack(start + 1); // 继续递归填充下一个位置
                swap(start, i); // 恢复当前位置的值,以便进行下一次交换
            }
        }
    backtrack(0); // 从第一个位置开始递归回溯
    return result;
};

上述代码中,我们同样定义了一个 backtrack 函数作为递归的核心实现。不同的是,这次我们通过 swap 函数来交换数组中的两个元素,从而实现了对数组的修改。在递归过程中,我们先交换当前位置与后面位置的值,然后继续递归填充下一个位置,最后再恢复当前位置的值,以便进行下一次交换。

交换法与递归回溯法相比,在代码实现上更加简洁,不需要创建新的数组对象,因此在空间复杂度上较优。但是,交换法在实际执行过程中需要频繁地进行元素交换操作,可能会导致性能稍差一些,尤其是在输入规模较大时。