这是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
函数来交换数组中的两个元素,从而实现了对数组的修改。在递归过程中,我们先交换当前位置与后面位置的值,然后继续递归填充下一个位置,最后再恢复当前位置的值,以便进行下一次交换。
交换法与递归回溯法相比,在代码实现上更加简洁,不需要创建新的数组对象,因此在空间复杂度上较优。但是,交换法在实际执行过程中需要频繁地进行元素交换操作,可能会导致性能稍差一些,尤其是在输入规模较大时。