解答过程:
/**
* @param {number[]} nums
* @return {number[][]}
递归+回溯
思路
1、每一位都有3种选择:1、2、3。
2、每一次都做选择,展开出一棵空间树,如下图。
3、利用约束条件「不能重复选」,做剪枝,剪去不会产生正确解的选项(分支)。
(1)利用 hashMap,记录选过的数,下次遇到相同的数,跳过。
(2)这样就不会进入「不会得出解的分支」,不做无效的搜索。
【1】结束当前递归时,将它加入 res 后,该算法还要进入别的递归分支继续搜索,还要继续将这个 path 传给别的递归调用,它所指向的内存空间还要继续被操作,所以 res 中的 path 的内容会被改变,这就不对。
*/
var permute = function(nums) {
const res = [];
const usedMap = {}; //哈希表,用于记录要选择的数有没有使用过
// 深度优先遍历的递归函数dfs
function dfs(path) {
// 递归出口:选择的路径长度满足了要求,刚好选满
if(path.length === nums.length) {
res.push(path.slice()); // 【1】
return; // 结束当前递归的路径选择
}
for(const num of nums) {
if(usedMap[num]) continue; // 使用过的直接跳过,避免无效路径
path.push(num); // 没有使用过的,加入paht的路径选择
usedMap[num] = true; // 同时将这个数标记为已使用
dfs(path); // 在选择了num的当前path路径上继续递归,进行路径选择,直到该条路径的递归结束;
path.pop(); // 上一条路径的递归结束,回溯:(1)将最后选择的数pop出去(2)将最后一个数的使用状态改为未使用;
usedMap[num] = false;
}
}
dfs([]); // 递归入口:dfs 执行传入空 path,什么都还没选
return res;
};
const _permute = string => {
if(string.length === 1){
return [string];
}
// 全排列容器
const res = [];
for(let s of string) {
// 获取除了当前元素外的其他元素字符串
const otherStr = string.split('').filter(item => item!==s).join('');
// 递归获取其余元素字符串的递归集合,并和当前元素进行拼接
_permute(otherStr).forEach(str => {
res.push(s + str);
})
}
return res;
}
可以用于字符串的处理,照搬过来用的话,处理不了数组的情况:
使用上述题目的递归+回溯的解法可以处理该题目,测试用例通过的解答:
const _permute = string => {
// 补全代码
const res = [];
const usedMap = {}; //哈希表,用于记录要选择的数有没有使用过
// 深度优先遍历的递归函数dfs
function dfs(path) {
// 递归出口:选择的路径长度满足了要求,刚好选满
if(path.length === string.length) {
res.push(path.slice()); // 【1】
return; // 结束当前递归的路径选择
}
for(const num of string) {
if(usedMap[num]) continue; // 使用过的直接跳过,避免无效路径
path.push(num); // 没有使用过的,加入paht的路径选择
usedMap[num] = true; // 同时将这个数标记为已使用
dfs(path); // 在选择了num的当前path路径上继续递归,进行路径选择,直到该条路径的递归结束;
path.pop(); // 上一条路径的递归结束,回溯:(1)将最后选择的数pop出去(2)将最后一个数的使用状态改为未使用;
usedMap[num] = false;
}
}
dfs([]); // 递归入口:dfs 执行传入空 path,什么都还没选
return res.map(item => item.join(''));
}