`

Permutation总结

阅读更多
有关排列的问题我们可以与组合比较来考虑,解决这类问题我们都采用回溯法,在组合中不用考虑顺序,而在排列中就要考虑顺序,例如(1,2,3)和(2,1,3),在组合的概念中是相等的,但它们是不同的排列。下面列举leetcode中有关permutations的几道题目。

1,Permutation
给定一个数组numbers ,输出所有可能的排列。
例如:numbers = {1,2,3};
输出:[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2],[3,2,1].

因为我们要考虑顺序,回溯的时候我们始终从第一个元素开始遍历,如果长度等于数组的长度我们就找到结果,保存这个结果,然后返回上一层,继续遍历。代码如下:
public class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> llist = new ArrayList<List<Integer>>();
        LinkedList<Integer> list = new LinkedList<Integer>();
        if(nums == null) return llist;
        permutation(0, nums, list, llist);
        return llist;
    }
    
    private void permutation(int start, int[] nums, LinkedList<Integer> list, List<List<Integer>> llist) {
        if(list.size() == nums.length){
            llist.add(new LinkedList<Integer>(list));
        }
        for(int i = start; i < nums.length; i++) {
            if(list.size() < nums.length && !list.contains(nums[i])) {
                list.add(nums[i]);
                permutation(0, nums, list, llist);
                list.removeLast();
            }
        }
    }
}


2,Permutation2
给定一个数组numbers, 给出所有可能的排列,数组中可能存在重复元素。
例如:numbers[] = {1,1,2}
输出:[1,1,2], [1,2,1], [2,1,1].

因为有重复元素,所以在判断添加元素时不能通过元素的值来判断,我们可以通过维护一个数组,来记录已经遍历过的元素的下标,只添加没遍历过的元素。代码如下:
public class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> llist = new ArrayList<List<Integer>>();
        LinkedList<Integer> list = new LinkedList<Integer>();
        LinkedList<Integer> usedIndex = new LinkedList<Integer>();
        if(nums == null) return llist;
        Arrays.sort(nums);
        permutationUnique(0, nums, list, usedIndex, llist);
        return llist;
    }
    
    private void permutationUnique(int start, int[] nums, LinkedList<Integer> list, LinkedList<Integer> usedIndex, List<List<Integer>> llist) {
        if(list.size() == nums.length) {
            llist.add(new LinkedList<Integer>(list));
        }
        int delete = Integer.MAX_VALUE;
        for(int i = start; i < nums.length; i++) {
            if(delete == nums[i]) continue;
            if(!usedIndex.contains(i)) {
                usedIndex.add(i);
                list.add(nums[i]);
                permutationUnique(0, nums, list, usedIndex, llist);
                usedIndex.removeLast();
                delete = list.removeLast();
            }
        }
    }
}


3,Next Permutation
给定一个排列,返回下一个比它大的排列。如果找不到比它大的元素,就返回排列中和最小的组合。
例如:
给定1, 2, 3  返回 1, 3, 2
给定3, 2, 1 返回 1, 2, 3
给定 1, 1, 5 返回 1, 5, 1

找到比它大的组合,我们不妨从后面遍历数组,找到第一个i元素,使nums[i] >nums[i-1], 找到这个位置如果直接将它们位置调换并不一定不符合提议,例如一个数组是{1,2,5,3},第一个nums[i] >nums[i-1] 的是5>2,如果直接将它们交换结果为{1,5,2,3},而正确结果应该为{1,3,2,5}。因此再找到这个i位置后,我们要遍历i元素后面的元素,如果有元素比nums[i-1]
大,就将它们位置互换,再将i后面剩余的元素按升序排列。实现代码如下:
public class Solution {
    public void nextPermutation(int[] nums) {
        if(nums == null) return;
        for(int i = nums.length - 1; i > 0; i--) {
            if(nums[i] > nums[i-1]) {
                int j = nums.length - 1;
                while(j > i-1 && nums[j] <= nums[i-1]) j --;
                int tem = nums[i-1];
                nums[i-1] = nums[j];
                nums[j] = tem;
                Arrays.sort(nums, i, nums.length);
                return;
            }
        }
        reverse(nums);  
    }
    
    public void reverse(int[] nums) {
        int l = 0;
        int r = nums.length -1;
        while(l < r) {
            int tem = nums[l];
            nums[l] = nums[r];
            nums[r] = tem;
            l ++;
            r --;
        }
    }
}


4,Permutation Sequence
给定一个整数n,n >= 1 && n <= 9; 在给定一个整数k,要求输出第k个排列。

如果我们用回溯法列出所有排列,然后取第k个排列。代码如下:
public class Solution {
   
    List<List<Integer>> llist = new ArrayList<List<Integer>>();
    
    public String getPermutation(int n, int k) {
        List<Integer> list = new ArrayList<Integer>();
        if(n == 0) return null;
        permutation( n, k, list);
        if(k <= llist.size()){
            String result = "";
            for(int i = 0; i < llist.get(k-1).size(); i++){
                result += llist.get(k-1).get(i).toString();
            }
            return result;
        }
             
        return null;
    }
    private void permutation( int n, int k, List<Integer> list){
        if(list.size() == n) {
            llist.add(new ArrayList<Integer>(list));
            return;
        }
        for(int i = 1; i <= n; i++){
            if(!list.contains(i)) {
                list.add(i);
                permutation(n, k, list);
                list.remove(list.size()-1);
            }
        }
    }
}


到这样会超时,我们完全可以直接计算出第k个排列。我们都知道n个元素全排列的结果为n!,我们把这n!个排列分成n组,每个组都有(n-1)!个,那么第k个元素在第几组呢,我们不妨通过简单的例子来分析一下,当n = 3时,第一组为{1,2,3},{1,3,2}; 第二组为{2,1,3},{2,3,1} ;第三组为{3,1,2},{3,2,1},因此当k等于1或2时属于第一组;等于3,4时属于第二组;等于5,6时属于第三组,符合(k-1)%(n-1)! ,这样我们就确定了第一个元素。我们令k = k/(n-1)!,循环操作,直到n为1位置,代码如下:
public class Solution {
    public String getPermutation(int n, int k) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        StringBuffer sb = new StringBuffer();
        int[] fac = new int[n];
        fac[0] = 1;
        for(int i = 1; i < n; i++) {
            fac[i] = fac[i-1] * i;
        }
        
        for(int i = 1; i <= n; i++) {
            list.add(i);
        }
        
        k = k - 1;
        for(int i = 0; i <= n - 1; i++) {
            int index = k /fac[n - 1 - i];
            sb.append(list.get(index));
            list.remove(index);
            k = k % fac[n - 1 - i];
        }
        return sb.toString();
    }
}
分享到:
评论

相关推荐

    Permutation with Repetition

    #### 总结 通过上述方法,我们可以有效地解决带重复的排列问题。这种方法不仅适用于本问题的具体实例,而且可以应用于更广泛的组合问题中。通过对排列生成的理解和实现,可以帮助我们更好地掌握递归算法的应用技巧...

    hutc-Permutation with Repetition参考代码

    #### 总结 该程序提供了一个完整的示例来演示如何使用递归方法解决全排列问题,尤其是在有重复元素的情况下。通过理解和应用这个程序,我们可以更好地掌握递归、回溯等算法的基本原理及其应用场景。此外,这个例子...

    Permutation with Repetition参考代码

    ### Permutation with Repetition 参考...#### 五、总结 通过上述代码实现了对有重复元素的集合进行排列,并有效去除了重复排列的问题。此方法不仅适用于小规模数据,而且对于题目规定的最大规模数据集也能高效处理。

    Two new permutation algorithms

    #### 总结与讨论 本文提出的两种新算法不仅丰富了排列生成领域的研究成果,而且为实际应用提供了更多选择。移动游标算法因其高效性和接近理论最优解的特点而显得尤为重要;而层级算法则因其能够有效生成随机排列而...

    Permutation Group Algorithms

    总结来说,置换群算法是数学和计算机科学领域中,特别是在群论和符号计算方面的一块基石。它对于理解群的深层结构和分类问题提供了强有力的工具,而几乎线性时间算法的提出和应用,极大地加速了群论问题的求解过程。...

    原位置换in situ permutation.pdf

    ### 原位置换(In Situ Permutation) 原位置换是计算机科学中的一种数据处理技术,它涉及将数组中的元素按照一定规则重新排列,而不使用额外的存储空间。换句话说,所有操作都在原数组上进行,不借助任何其他数组...

    Problem B:Permutation with Repetition 分值算法

    ### Permutation with Repetition 算法设计与分析 ...#### 总结 本文详细介绍了“有重复的排列”问题的解决方法,包括问题描述、算法设计、代码实现等环节,帮助读者全面理解并掌握这类问题的解决思路。

    Differential Evolution: A Handbook for Global Permutation-Based Combinatorial Optimization

    #### 六、总结 《Differential Evolution: A Handbook for Global Permutation-Based Combinatorial Optimization》这本书深入探讨了差分进化算法在解决全局排列组合优化问题方面的理论基础和技术细节。通过理解...

    Color image encryptionn_image_

    总结来说,"Color image encryption based on joint permutation and diffusion"是一种结合了置换和扩散的高效彩色图像加密方法,它通过打乱像素顺序和扩散像素值来增强图像的安全性,为数字图像的保护提供了一种...

    (ABCDE)字符串排序

    总结来说,"(ABCDE)字符串排序"是一个关于使用C++生成字符串全排列的问题,涉及到递归算法和排列的性质。通过`string_permutation`函数,我们可以有效地枚举一个字符串的所有可能排列,这在很多实际问题中,如数据...

    Permutation-Combination-Calculator

    在这个"Permutation-Combination-Calculator"项目中,开发者很可能创建了一个用户友好的界面,允许用户输入元素数量和选择类型(排列或组合),然后使用上述算法计算结果并显示出来。为了确保程序的兼容性,至少需要...

    Permutation with Repetition R={ r1,r2,… ,rn }是要进行排列的n 个元素。其中元素r1,r2,… ,rn可能相同。试设计一个算法,列出R的所有不同排列。

    ### 重复元素排列问题 #### 问题描述 本问题是关于如何设计一个...#### 总结 通过上述方法,我们能够有效地解决具有重复元素的排列问题。此算法的关键在于避免了生成重复排列的可能性,从而保证了输出结果的正确性。

    lrucacheleetcode-LeetCode:这个库用于总结leetcode中遇到的习题,期望按照数据结构和常用方法分成2类,进行总结,

    这个库用于总结leetcode中遇到的习题 常用数据结构习题总结 1.线性表 解决进度 No. Describition mark 1 Remove Duplicates from Sorted Array 2 Remove Duplicates from Sorted Array II 3 Search in Rotated ...

    蓝桥杯大题总结(历届比赛共40多大题) java 版

    ### 蓝桥杯大题总结之Java版详解 #### 一、全排列问题解析 全排列是指将一组元素的所有可能顺序重新排列的一种数学概念。对于一个包含n个不同元素的集合,其全排列总数为n!(阶乘)。例如,若给定集合{A, B, C},...

    A Reconfigurable Parallel Accelerator for Permutation Entropy

    本文介绍了一种可配置的...总结来说,本文提出的可配置FPGA加速器在计算多尺度置换熵方面展示了巨大的潜力,尤其是在需要实时、高效处理大量EEG信号的应用场景中,展现了硬件加速器在生物医学信号处理方面的应用前景。

    C++中STL使用总结

    本文将总结C++中STL的使用,涵盖vector、stack、queue等常用容器。 一、vector向量容器 Vector是一种可以动态扩展的数组,它支持快速的随机访问,并允许在序列末尾插入和删除元素。 1. 创建vector对象:不指定...

    DES加密算法实验报告

    #### 三、总结 通过本次实验,学生不仅能够深入了解DES加密算法的基本原理,还能亲自动手编写代码实现加密解密过程,这对于加深理论知识的理解、提高实践能力具有重要意义。此外,通过对DES算法的学习,还可以引申...

    ACM算法总结

    ACM 算法总结 ACM(Association for Computing Machinery,计算机学会)算法是计算机科学和信息技术领域中的一个重要分支,它们广泛应用于数据结构、算法设计、数学模型等领域。下面是对 ACM 算法的一些重要知识点...

Global site tag (gtag.js) - Google Analytics