BM56 有重复项数字的全排列

描述

给出一组可能包含重复项的数字,返回该组数字的所有排列。结果以字典序升序排列。

数据范围: 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题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值