描述
给出一组可能包含重复项的数字,返回该组数字的所有排列。结果以字典序升序排列。
数据范围: 0 < n \le 80<n≤8 ,数组中的值满足 -1 \le val \le 5−1≤val≤5
要求:空间复杂度 O(n!)O(n!),时间复杂度 O(n!)O(n!)
前言
这一题和第55题大同小异,第55题是无重复的全排列,这一题是原数据里有重复数字的全排列,故而这两题一起写。
分析
全排列组合顾名思义就是把数组中所有数据相互之间的组合,提取出来,本题中去掉相同的排列之后得到的数据就是我们需要的数据。时间复杂度是 num.length ** num.length。遍历过程中我们需要拿当前数据和数组中其他数据组合。这里涉及高中数学知识,俺已经交还给老师了,感兴趣的,可以百度一下 全排列组合
55题
这一题是没有重复数据的全排列组合。刚开始的时候,没有把自己排除,得到了很多包含自己的组合。
const num = [1, 2, 3];
const res = [];
num.sort((a, b) => a - b);
dfs([], num);
function dfs(path, param) {
if (path.length === param.length) {
res.push([...path]);
log(path, '#app');
return;
}
param.forEach((item, index)=> {
path.push(item);
dfs(path, param);
path.pop();
});
}
得到结果
代码解释
拿到数据咱还是先排个序,方便结果也是排序的。
dfs方法有两个参数,(path, num)
path 临时缓存的数组
num 需要全排列的数组
每次循环用当前数据和其他数据组合,然后层层回调。
终止条件是当缓存数组长度等于原数据长度时,保存当前数组,结束当次递归。在递归结束后要删除缓存数组里用过的数据,以保证不会出现意外。
log方法是为了将结果显示在浏览器页面,便于显示。
自己和自己组合不复合题目要求,我们要排除自己。新定义一个has表,map 也行,标记自己是否被选过
const has = {};
如果选过直接结束当次循环,如果没有选过,就把当前数据缓存起来,并且标记当前数据已选过,然后继续回调,回调结束后取消上一次标记。
if (has[index]) {
return ;
} else {
path.push(item);
has[index] = true;
dfs(path, param);
has[index] = false;
path.pop();
}
因为55题,原数据里是没有重复的,我们使用索引和值本身标记都是可以的。如同这样
param.forEach((item, index)=> {
if (has[item]) {
return ;
} else {
path.push(item);
has[item] = true;
dfs(path, param);
has[item] = false;
path.pop();
}
});
全部代码
/**
*
* @param num int整型一维数组
* @return int整型二维数组
*/
function permute( num ) {
// write code here
const res = [];
const has = {};
num.sort((a, b) => a - b);
dfs([], num);
function dfs(path, param) {
if (path.length === param.length) {
res.push([...path]);
return;
}
param.forEach((item, index)=> {
if (has[item]) {
return ;
} else {
path.push(item);
has[item] = true;
dfs(path, param);
has[item] = false;
path.pop();
}
});
}
return res;
}
module.exports = {
permute : permute
};
然后第55题就可以过啦,
56题
56题与55题不同就在于原数据是有重复的。使用第55题的代码是无法通关的。在55题的基础上,我们需要对结果去重,想到的还是哈希表,这一次我们使用set;
const mySet = new Set();
保存一下原结果,
const tempPlan = JSON.stringify(path);
!mySet.has(tempPlan) && mySet.add(tempPlan);
排列完后恢复结果, 存到res数组中,
mySet.forEach(item => {
res.push(JSON.parse(item));
})
返回结果
return res;
全部代码,需要注意的是,56题的原数据是有重复的,标记的时候要使用索引,不要使用值,避免不可预测。
/**
*
* @param num int整型一维数组
* @return int整型二维数组
*/
function permuteUnique( num ) {
// write code here
const res = [];
const has = {};
const mySet = new Set();
num.sort((a, b) => a - b);
dfs([], num);
function dfs(path, param) {
if (path.length === param.length) {
const tempPlan = JSON.stringify(path);
!mySet.has(tempPlan) && mySet.add(tempPlan);
return;
}
param.forEach((item, index)=> {
if (has[index]) {
return ;
} else {
path.push(item);
has[index] = true;
dfs(path, param);
has[index] = false;
path.pop();
}
});
}
mySet.forEach(item => {
res.push(JSON.parse(item));
})
return res;
}
module.exports = {
permuteUnique : permuteUnique
};
也许还可以再优化一下,使用set去重在排列完之后恢复数据,我们可以省略这一步,放在排列里。
if (path.length === param.length) {
const tempPlan = JSON.stringify(path);
if (!mySet.has(tempPlan)) {
mySet.add(tempPlan);
res.push([...path]);
}
return;
}
如果set里没有就直接存入,需要注意的是缓存的path数组在递归完之后,是需要删除缓存的,在保存该组合时需要深复制。
全部代码
/**
*
* @param num int整型一维数组
* @return int整型二维数组
*/
function permuteUnique( num ) {
// write code here
const res = [];
const has = {};
const mySet = new Set();
num.sort((a, b) => a - b);
dfs([], num);
function dfs(path, param) {
if (path.length === param.length) {
const tempPlan = JSON.stringify(path);
if (!mySet.has(tempPlan)) {
mySet.add(tempPlan);
res.push([...path]);
}
return;
}
param.forEach((item, index)=> {
if (has[index]) {
return ;
} else {
path.push(item);
has[index] = true;
dfs(path, param);
has[index] = false;
path.pop();
}
});
}
return res;
}
module.exports = {
permuteUnique : permuteUnique
};
使用set表保存增加了一块内存,那么还能不能在优化一下呢。假设不用set。
使用条件判断,在缓存path时排除重复的。
因为排过序了,相同的值会在一起,如果当前值等于下一个,并且上一个值未标记,那么跳过当前值。
if (index > 0 && item === num[index - 1] && !has[index - 1]) {
return ;
}
同样使用has标记查找过的值。排除和has标记要分开判断,避免不可预测。
if (!has[index]){
path.push(item);
has[index] = true;
dfs(path, param);
has[index] = false;
path.pop();
}
全部代码
/**
*
* @param num int整型一维数组
* @return int整型二维数组
*/
function permuteUnique( num ) {
// write code here
const res = [];
const has = {};
const mySet = new Set();
num.sort((a, b) => a - b);
dfs([], num);
function dfs(path, param) {
if (path.length === param.length) {
res.push([...path]);
return;
}
param.forEach((item, index)=> {
if (index > 0 && item === num[index-1] && !has[index-1]) {
return ;
}
if (!has[index]){
path.push(item);
has[index] = true;
dfs(path, param);
has[index] = false;
path.pop();
}
});
}
return res;
}
module.exports = {
permuteUnique : permuteUnique
};
这样就可以通过第56题了。