题解 | #有重复项数字的全排列#

有重复项数字的全排列

https://www.nowcoder.com/practice/a43a2b986ef34843ac4fdd9159b69863

如何思考剪枝过程?

  • 数组中有重复数字会导致出现重复的排列,因为要对原数组循环遍历每个数字来递归获取排列数。
  • 要阻止出现重复排列的情况,就要在同一层循环中不使用重复的数字来递归,为了方便判断是否为重复的数字,可以先对数组进行排序,再判断相邻的两个数字是否重复即可。
  • used数组用于判断某个数字是否被使用过,如果被使用过则跳过这个数字避免重复使用。但在重复数字的场景下,如果数字没被使用过还需要判断他是否是跟前一个数字重复,如果重复了也要跳过这个数字。
  • used数组只在递归过程会逐个变true,如果used[i-1]为false说明和当前数字相同的上一个数字在上一次循环中被递归使用了,回溯后当前层为false,所以如果当前数字和前一个数字相同,就不能再选择这个数字进入递归。
  • 因为要判断两个相邻的数字,所以起码要从第二个数字开始判断,即i>0。

参考

https://blog.nowcoder.net/n/50274e5a9c4142be9fa551fba14b6f6a?f=comment

/**
 * 
 * @param num int整型一维数组 
 * @return int整型二维数组
 */
function permuteUnique( num ) {
    const ret = []
    const used = Array.from(num, (item) => item = false);  // 用于判断数组中的元素是否使用过,如果使用过则跳过当前元素
    num.sort();  // 为了能方便的判断两个相邻数字是否相同,需要先对数组进行排序
    // write code here
    function dfs(num, ansArray=[]) {
        if (ansArray.length === num.length) {
            ret.push(ansArray.slice());  // 要传入一个副本,也可以用[...ansArray]
            return;
        }
        for (let i = 0; i < num.length; ++i) {
            // 有重复数字后会导致出现重复的排列,要阻止这种情况则必须要同一层中重复数字不能重复递归使用,关键是如何判断同一层重复数字的逻辑。
            // used数组只在递归过程会逐个变true,如果当前为true上一个为false说明这是同一层循环中的上一次循环回溯后变false了,此时两者在同一层。
			// 因为要判断两个相邻的数字,所以起码要从第二个数字开始判断,即i>0
            if (used[i] || (i > 0 && num[i-1] === num[i] && !used[i-1])) {  
                continue;
            }
            
            ansArray.push(num[i]);
            used[i] = true;
            dfs(num, ansArray);  // 递归传入的是原数组,通过判断原数组的元素是否使用过来缩减元素范围,而不是通过传入削减后的数组切片,这样会丢失元素
            used[i] = false;
            ansArray.pop();
        }
    } 

    dfs(num, []);

    return ret;
}
module.exports = {
    permuteUnique : permuteUnique
};
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param num int整型一维数组 
     * @return int整型ArrayList<ArrayList<>>
     */
    private ArrayList<ArrayList<Integer>> res = new ArrayList<>();
    public ArrayList<ArrayList<Integer>> permuteUnique (int[] num) {
        // write code here
        boolean[] used = new boolean[num.length];
        ArrayList<Integer> arr = new ArrayList<Integer>();
        Arrays.sort(num);
        dfs(num, arr, used);

        return this.res;
    }

    private void dfs(int[] num, ArrayList<Integer> arr, boolean[] used) {
        if (num.length == arr.size()) {
            this.res.add(new ArrayList<Integer>(arr));
            return;
        }

        for (int i = 0; i < num.length; ++i) {
            if (used[i] || (i > 0 && num[i] == num[i-1] && used[i-1] == false)) {
                continue;
            }

            arr.add(num[i]);
            used[i] = true;
            dfs(num, arr, used);
            used[i] = false;
            arr.remove(arr.size()-1);
        }

    }
}

全部评论

相关推荐

牛马43373018...:这人真懂什么叫熵吗
点赞 评论 收藏
分享
原来已经一年了,因为没有加任何实验室没有学长学姐带,再一次偶然的机会下刷到我们学校的牛肉哥,和他聊天之后发现他也没加实验室能进大厂,我就燃起了希望,去年大概&nbsp;4&nbsp;月份找好路线&nbsp;零基础&nbsp;开始学&nbsp;5&nbsp;月背八股和开始刷算法很难受&nbsp;7-8&nbsp;月焦虑躯体化害怕找不到实习&nbsp;9&nbsp;月找到一家像样的小厂去实习了&nbsp;4&nbsp;个月大三上期末考试结束之后&nbsp;1&nbsp;月份回来边实习边准备工作压力很大&nbsp;当时只有字节、百度、商汤的面试,字节三面挂了,百度&nbsp;oc,商汤&nbsp;二面挂(差评&nbsp;无效面试),之后来深圳百度实习之后还是觉得不甘心一直没把算法和八股扔下一直在准备,百度实习的时候&nbsp;mt&nbsp;交给我一个特别重要的工作数据库迁移(特别感谢&nbsp;mt&nbsp;,这个需求学到了很多东西处理了一堆线上问题),本来看着暑期他们面试都很困难,然后听说百度要涨实习薪资(然而&nbsp;5&nbsp;月并没有涨),就想着留在百度吧也懒得面试了,4&nbsp;月&nbsp;20&nbsp;多的时候字节&nbsp;hr&nbsp;打电话约面问我要不要尝试一下询问了&nbsp;1&nbsp;月份三面为啥会挂有没有学习&nbsp;ai&nbsp;知识(因为字节这边后端岗位偏&nbsp;ai),我来到百度之后全面拥抱&nbsp;AI&nbsp;也认识了我的好兄弟&nbsp;X&nbsp;哥,他在百度&nbsp;XX&nbsp;部门&nbsp;Agent&nbsp;实习,他属于是我&nbsp;Agent&nbsp;的启蒙老师,来百度之后一直在了解&nbsp;AI&nbsp;这一块,我就接受了字节的面试,一面的时候&nbsp;20&nbsp;分钟实习拷打然后突然说&nbsp;30&nbsp;分钟代码考核我心就凉了以为是&nbsp;kpi,算法题是手撕高并发安全下的令牌桶限流器,我写了整整&nbsp;80&nbsp;多行代码最后也写出来了,但是从来没看到过出这种题能&nbsp;oc&nbsp;的我也就不管了,后边面试也是很顺利但是流程有点长可能一直在横向吧总结结果是好的!!!感谢这一年努力的自己和遇到的各位互联网大佬分享的知识!!!ps&nbsp;图二纯感慨&nbsp;(觉得🍬请不要喷我)欢迎大家一起交流学习呀!!!!
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务