一个关于含有可重复元素的排列问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个关于含有可重复元素的排列问题相关的知识,希望对你有一定的参考价值。

一个关于含有可重复元素的排列问题例如:已知数字3、2、2,其排列个数n=(3的阶乘)/(1的阶乘*2的阶乘)=3。

为什么我看不懂这个结论呢?还请大家帮我解释一下这个结论,谢谢!

n个位置取n1个位置给第1个元素有C(n,n1)种取法,
n-n1个位置取n2个位置给第2个元素有C(n-n1,n2)种取法,
n-n1-n2个位置取n3个位置给第3个元素有C(n-n1-n2,n3)种取法,
.....
所以排列个数=C(n,n1)*C(n-n1,n2)*C(n-n1-n2,n3)*....=结论
参考技术A 随便买一本离散的书,上面就有具体证明,以你上面的公式来说
S中有n1个a1,那么在S的全排列中要占据n1个位置,选法有C(n,n1)种
然后,剩下n-n1个位置里要选n2个组合,有C(n-n1,n2)种方法
。。。
把这些取法用乘法原则乘起来,就是一共有几种排列法
C(n,n-n1)*C(n-n1,n2)*.....以第一第二项为例,会发现一个分母上有(n-n1)!一个分子上有,就约掉了,最后剩下的就是那个结论的式子
参考技术B 写的这么清楚明了,你看不懂?智商啊追问

我倒是知道遇到这类问题,直接套这个公式就行,就是不知道原理呀!

追答

不用知道

追问

😅

全排列 II(力扣第47题)

题目:给定一个可包含重复数字的序列,返回所有不重复的全排列。

示例:

输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

分析:

  这个题和全排列一的不同之处在于,它给定的一组元素含有重复的元素存在,那么我们进行深度递归搜索的时候,选择下一步搜索的元素的时候就要修改原先再全排列一中的判断条件了。那么满足什么条件,才能作为当前元素的下一步搜索选择呢?

  首先我们从最开始进行分析,给定一组带有重复的元素,一个含有n个,最开始的可选的元素个数为去重之后的元素个数,比如[1,1,2],那么开始时,可选的元素个数为2个,即1和2,假设选定1作为其实搜索元素,那么继续向下搜索,从1走向下一步,我们再来选择,要求必须是未被访问的,且去重之后的,此时可选的范围还是两个,即1和2,假设选定1;继续搜索,只有2了,那就选定2。此时得到一种排列组合:1,1,2

  总结一下,在这个题目中,选择作为下一步搜索元素的条件有两个:

    (1)此元素未被访问过;

    (2)此元素只能在候选范围中出现一次;

  那么如何解决这个去重问题呢,我的想法是借助哈希表,在遍历当前元素的所有的可走的下一步元素时,先判断此元素在哈希表中是否存在,如果存在,说明其已经在候选范围中出现了,那么就跳过,否则就可以作为候选,走向这个元素进行下一步搜索。

  注意,我说的这个候选范围是指,对于当前正在遍历的元素来说,如果岂不是最后一个点,其必然要向下进行搜索,那么向下搜索的时候最少有一条路可走,而我们要对这些可走的路根据题意设定条件进行筛选,全排列一给定的一组元素是不重复的,所以只要这组元素中有未被访问的点那么就能入选向下搜索的候选元素,但是现在有了重复元素,所以对于当前元素来说,其向下搜索时不设定去重条件,那么其向下搜索的候选元素就会有重复的元素,那么最终也会造成元素排列的重复,所以我们要进行去重,去重的方法就是利用哈希表,每次向下搜索前先判断这个元素是否在哈希表中存在,如果存在那么就说明候选元素已经有此元素了,并且已经可能产生排列结果了,当前这个就没有必要继续访问了,直接跳过即可。

 

    private List<List<Integer>> reslist;
    public List<List<Integer>> permuteUnique(int[] nums) {

        if (nums.length == 0 || nums == null){
            return new ArrayList<>();
        }
        int n = nums.length;
        boolean[] isVisited = new boolean[n];
        reslist = new ArrayList<List<Integer>>();
        List<Integer> sortres = new ArrayList<>();
        findAllSort(nums,isVisited,sortres);


        return reslist;
    }

    private void findAllSort(int[] nums,boolean[] isVisited, List<Integer> sortres) {

        if (sortres.size() == nums.length){
            reslist.add(new ArrayList<>(sortres));
            return;
        }

        HashMap<Integer, Integer> isSame = new HashMap<>();

        for (int i = 0; i < isVisited.length; i++) {

            if (isVisited[i] || isSame.get(nums[i])!=null){
                continue;
            }

            isSame.put(nums[i],1);
            isVisited[i] = true;

            sortres.add(nums[i]);
            findAllSort(nums,isVisited,sortres);
            sortres.remove(sortres.size()-1);
            isVisited[i] = false;
        }

    }

  我的程序运行的时间效率一般,只超过了百分之四十多的人,所以学习借鉴一下cyc2018大神的代码,它判断当前要向下搜索的元素是否是重复的,是先进行了排序,然后判断这个元素是否等于排序后的数组中的前一个元素,如果等于并且前一个元素还未被访问,那么就跳过这个元素,否则可以访问。他的程序运行效率很高,超过了百分之百的人。

    List<List<Integer>> permutes = new ArrayList<>();
    List<Integer> permuteList = new ArrayList<>();
    Arrays.sort(nums);  // 排序
    boolean[] hasVisited = new boolean[nums.length];
    backtracking(permuteList, permutes, hasVisited, nums);
    return permutes;
}

private void backtracking(List<Integer> permuteList, List<List<Integer>> permutes, boolean[] visited, final int[] nums) {
    if (permuteList.size() == nums.length) {
        permutes.add(new ArrayList<>(permuteList));
        return;
    }

    for (int i = 0; i < visited.length; i++) {
        if (i != 0 && nums[i] == nums[i - 1] && !visited[i - 1]) {
            continue;  // 防止重复
        }
        if (visited[i]){
            continue;
        }
        visited[i] = true;
        permuteList.add(nums[i]);
        backtracking(permuteList, permutes, visited, nums);
        permuteList.remove(permuteList.size() - 1);
        visited[i] = false;
    }
}

 

以上是关于一个关于含有可重复元素的排列问题的主要内容,如果未能解决你的问题,请参考以下文章

作业2.有重复全排列和无重复全排列的区别

全排列 II(力扣第47题)

排列组合中元素有相同的怎么办

有重复全排列

力扣:包含重复元素的全排列问题

剑指Offer 38 - 字符串的排列