输出字符串中字符的所有排列

编写带有下列声明的例程:

public void permute(String str);
private void permute(char[] str, int low, int high);

第一个例程是个驱动程序,它调用第二个例程并显示String str中的字符的所有排列。例如,str"abc", 那么输出的串则是abcacbbacbcacabcba,第二个例程使用递归。

本文主要参考了字符串全排列算法学习-低调小一

这题主要使用到递归,递归的四条基本法则(数据结构与算法分析-Java语言描述page7):
1、基准情形。必须总要有某些基准情形,无需递归就能解出。
2、不断推进。对于那些需要递归求解的情形,每一次递归调用都必须要使状况朝向一种基准情形推进。
3、设计法则。假设所有的递归调用都能运行。
4、合成效益法则。在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。

对于字符串的排列问题:
如果能生成n-1个元素的全排列,就能生成n个元素的全排列。对于只有一个元素的集合,可以直接生成全排列。所以全排列的递归终止条件很明确,只有一个元素时。我们可以分析一下全排列的过程:
(1) 首先,我们固定第一个字符a,求后面两个字符bc的排列。
(2) 当两个字符bc排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列。
(3) 现在是把c放在第一个位置的时候了,但是记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍是和原先处在第一个位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处于第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列。
(4) 既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了。

下面这张图很清楚的给出了递归的过程:
输出字符串中字符的所有排列_第1张图片

public static void permute(String str){
    char[] ch = str.toCharArray();
    permute(ch, 0, ch.length-1);
}

private static void permute(char[] str, int low, int high){
    int length = str.length;
    if(low == high){
        String s = "";
        for(int i=0;istr[i];
        }
        System.out.println(s);
    }
    for(int i=low; istr, i, low);
        permute(str, low+1, high);
        swap(str, i, low);
    }
}

public static void swap(char[] str, int m, int n){
    char temp = str[m];
    str[m] = str[n];
    str[n] = temp;
}

由于全排列就是从第一个数字起,每个数分别与它后面的数字交换,我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。但是对bab,第二个数和第三个数不同,则需要交换,得到bba。由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。

换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!

这样,我们得到在全排列中去掉重复的规则:
去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换
在原代码中加入如下改进代码:

for(int i=low; iif(is_swap(str, low, i) == 1){
        swap(str, low, i);
        permute(str, low+1, high);
        swap(str, low, i);
    }
}

其中is_swap(char[] str, int m, int n)函数如下。

public static int is_swap(char[] str, int m, int n){
    int flag = 1;
    for(int i=m;i1;
        if(str[i] == str[n]){
            flag = 0;
            break;
        }
    }

    return flag;
}

至此该问题得到解决。

你可能感兴趣的:(数据结构与算法分析)