常见算法之全排列 全组合

全排列算法是一种比较常考的算法,他的做法也比较多样。

首先我们来看看最符合我们直观思考的,思路是这样的:假如没有重复元素时,传入一个数组A,并插入到另外一个数组B中,假如B中已经包含这个元素,则跳过,否则插入数组B。我们来看看具体代码:

<span style="font-size:14px;">public static void permutation1(final String str, String buffer){
        if (str.length() == buffer.length()){
            System.out.println((num++) + ":" + buffer);
            return;
        }

        for (int i = 0; i < str.length(); i++){
            if (buffer.indexOf((int)str.charAt(i)) < 0){
                permutation1(str, buffer + str.charAt(i));
            }
        }
    }</span>

这个看代码就比较容易理解,所以就不多说了,它有缺点,就是不能有重复,那我们改一改,给每一个值都安置一个状态位,假如插入过置为1,没有则是0,所以,我们又有了第二种方法:

<span style="font-size:14px;">public static void permutation2(final char[][] array, String result, int len){

        if (result.length() == len){
            System.out.println(result);
        }else{
            for (int i = 0; i < len; i++){
                if (array[i][1] == 0){
                    array[i][1] = 1;
                    permutation2(array, result + array[i][0], len);
                    array[i][1] = 0;
                }
            }
        }
    }</span>

当然它也有缺点,我们需要在这之前对他传入数组进行转化。例如

<span style="font-size:14px;">public static void main(String[] args) {
        String str = "abcd";
        char[][] array = new char[str.length()][2];
        for (int i = 0; i < str.length(); i++){
            array[i][0] = str.charAt(i);
            array[i][1] = 0;
        }
        String result = new String();
        permutation2(array, result, str.length());
    }</span>

现在还有另外一种递归方法,假如我们的数组是abc 那么全排列的话有abc,acb,bac,bca,cba,cab。

也就是说,a开头的和{b,c}的全排列,b开头的和{a,c}的全排列,c开头的和{a,b}全排列。

p = {r1,r2,r3,r4...} , 设 pn = p - {rn}

perm(p) = r1perm(p1) + r2perm(p2) + r3perm(p3) + ....

可以看出每一个全排列可以继续分成更多的子全排列,而每个子排列可以使看成第一个字母与别的字母调换位置得来的。所以,我们还可以用以下代码求结果:

<span style="font-size:14px;">public static void swap(char[] array, int from, int to){
        char temp = array[from];
        array[from] = array[to];
        array[to] = temp;
    }

    public static void permutation3(char[] array, int n){
        if (n == array.length){
            System.out.println(new String(array));
        }else {
            for (int i = n; i < array.length; i++){
                swap(array, i, n);
                permutation3(array, n + 1);
                swap(array, i, n);
            }
        }
    }</span>

但是呢,我们介绍的全部都是递归的算法,想要非递归怎么办呢。

首先我们来看这样一个字符串1234,需要他的全排列,怎么求呢,1243,1342依此类推就可以得出全部了,但是,这依此类推是怎么类推法。首先,我们规定需要将传入的字符串进行排序,小的在前大的在后。然后我们需要从前想后找前面的数小于后面的数的点,我们先叫他替换点,例如:938740,从后往前找,3是一个替换点。找到替换点之后,我们继续从后往前找,找到第一个大于他的数,依旧是上面这个例子:937840,那这个数就是4了。好了,现在将他们进行替换,现在这个数变成947830了。然后我们需要把它从替换点这,进行反转,把7840转为0478,并与之前的数进行合并930478。然后从重复这个动作,就能找到全部的数了。

<span style="font-size:14px;">public static void reversal(char array[], int from, int to){
        while (from < to){
            swap(array, from++, to--);
        }
    }

    public static boolean hasNext(char[] array){
        if (array.length == 0 || array == null){
            return false;
        }

        int endIndex = array.length - 1;
        int q = endIndex - 1;
        int p = endIndex;
        while (q >= 0){
            if (array[q] < array[q + 1]){
                while (array[q] > array[p]){
                    p--;
                }
                swap(array, p, q);
                reversal(array, q + 1, array.length - 1);
                return true;
            }
            q--;
        }
        reversal(array, 0, array.length - 1);
        return false;

    }

    public static void main(String[] args) {
        char[] array = "abc".toCharArray();
        do {
            System.out.println(array);
        } while (hasNext(array));
    }</span>

但是,但是,但是,假如有是有重复字符的字符串,那要怎么办呢。还有假如某些字符是需要按照某种顺序呢,我表示我还在想,假如你知道的话,欢迎知道留言邮件都行:[email protected]

以上是全排列,下面我们来说全组合

首先我们来看个例子,p={a,b,c}的全组合:asse(p) = {},{a},{b},{c},{ab}.....

到这我们似乎可以看到一些规律:

{} => 000
{a} => 001 {b} => 010  
...

我们可以把他们和对一一对应,如此,我们一个值在0~size(asse(p))的值就必定代表一个唯一的值。

所以我们可以:

<span style="font-size:14px;">public static void assembly(char[] array){
        int num;                //全组合的组数
        num = 1 << array.length;
        for (int i = 0; i < num; i++){
            StringBuffer buffer = new StringBuffer();
            for (int j = 0; j < array.length; j++){
                if ((i & (1 << j)) > 0){
                    buffer.append(array[j]);
                }
            }
            System.out.println(buffer);
        }
    }</span>
时间: 2024-12-12 10:40:30

常见算法之全排列 全组合的相关文章

高效率的全组合算法(Java版实现)

博客上看到的一个算法,用Java实现了一个 算法描述: 算法说明:当n大于2时,n个数的全组合一共有(2^n)-1种. 当对n个元素进行全组合的时候,可以用一个n位的二进制数表示取法. 1表示在该位取,0表示不取.例如,对ABC三个元素进行全组合,  100表示取A,010表示取B,001表示取C,101表示取AC  110表示取AB,011表示取BC,111表示取ABC 注意到表示取法的二进制数其实就是从1到7的十进制数 推广到对n个元素进行全排列,取法就是从1到2^n-1的所有二进制形式 要

算法:全组合算法

public static List<int[]> combin(final int TOTAL, final int SELETED) { List<int[]> list = new ArrayList<int[]>(400000); int[] i = new int[SELETED]; for (int x = 1; x <= SELETED; x++) i[x - 1] = x; final int LAST = SELETED - 1; while (

全组合算法

//全组合算法        public static List<List<T>> FullCombination<T>(List<T> lstSource)        {            var n = lstSource.Count;            var max = 1 << n;            var lstResult = new List<List<T>>();           

全排列和全组合实现

记得@老赵之前在微博上吐槽说,“有的人真是毫无长进,六年前某同事不会写程序输出全排列,昨天发邮件还是问我该怎么写,这时间浪费到我都看不下去了.” 那时候就很好奇全排列到底是什么东西,到底有多难? 今天复习的时候终于碰到这题了,结果果然自己太渣,看了好久都没明白,代码实现又是磕磕碰碰的.所以,就把它整理成笔记加深记忆,也希望能帮到和我一样的人. 全排列 所谓全排列,就是打印出字符串中所有字符的所有排列.例如输入字符串abc,则打印出 a.b.c 所能排列出来的所有字符串 abc.acb.bac.b

全排列,全组合(dfs)

全排列 #include <iostream> #include <cmath> #include <cstdio> #include <cstring> #include <string> #include <map> #include <iomanip> #include <algorithm> #include <queue> #include <stack> #include &

字符串的全排列和全组合

输入:abc 输出:bac,cba,acb,bca,cab,abc 全排列的问题: public ArrayList<String> Permutation(String str) { ArrayList<String> list = new ArrayList<String>(); if(str!=null && str.length()>0){ helper(str.toCharArray(),list,0); Collections.sort

常见算法用Pascal实现

基本算法    这些都是非常基本的的算法,希望所有学习的人都能理解!        1.数论算法      求两数的最大公约数      function gcd(a,b:integer):integer;      begin      if b=0 then gcd:=a      else gcd:=gcd (b,a mod b);      end ;           求两数的最小公倍数      function lcm(a,b:integer):integer;      beg

五大常见算法策略——递归与分治策略

摘要:递归与分治策略是五大常见算法策略之一,分治策略的思想就是分而治之,即先将一个规模较大的大问题分解成若干个规模较小的小问题,再对这些小问题进行解决,得到的解,在将其组合起来得到最终的解.而分治与递归很多情况下都是一起结合使用的,能发挥出奇效(1+1>2),这篇文章我们将先从递归说起,再逐渐向分治过渡,主要讲解方式是通过9个例题来说明问题的,问题都是根据难度由简到难,由浅入深,对递归与分治能有个大概的了解雏形,当然最重要还是要做大量练习才能掌握. 1.递归 我们第一次接触递归一般都是在初学C语

acm常见算法及例题

转自:http://blog.csdn.net/hengjie2009/article/details/7540135 acm常见算法及例题 初期:一.基本算法:     (1)枚举. (poj1753,poj2965)     (2)贪心(poj1328,poj2109,poj2586)     (3)递归和分治法.     (4)递推.     (5)构造法.(poj3295)     (6)模拟法.(poj1068,poj2632,poj1573,poj2993,poj2996)二.图算法