首页 > 代码库 > 全排列算法(转)

全排列算法(转)

列出全排列的初始思想:

解决一个算法问题,我比较习惯于从基本的想法做起,我们先回顾一下我们自己是如何写一组数的全排列的:1,3,5,9(为了方便,下面我都用数进行全排列而不是字符)。

1,3,5,9.(第一个)

首先保持第一个不变,对3,5,9进行全排列。

同样地,我们先保持3不变,对5,9进行全排列。

保持5不变,对9对进行全排列,由于9只有一个,它的排列只有一种:9。接下来5不能以5打头了,5,9相互交换,得到

1,3,9,5.

此时5,9的情况都写完了,不能以3打头了,得到

1,5,3,9

1,5,9,3

1,9,3,5

1,9,5,3

这样,我们就得到了1开头的所有排列,这是我们一般的排列数生成的过程。再接着是以3、5、9打头,得到全排列。这里还要注意的一点是,对于我们人而言,我们脑子里相当于是储存了一张表示原有数组的表,1,3,5,9,1开头的所有排列完成后,我们选择3开头,3选完了之后,我们选择5开头,而不会再返过来选1,而且知道选到9之后结束,但对于计算机而言,我们得到了3,5,1,9后,可能再次跳到1当中,因为原来数组的顺序它已经不知道了,这样便产生了错误。对于算法的设计,我们也可以维护这样一个数组,它保存了原始的数据,这是一种方法同时我们还可以再每次交换后再交换回来,变回原来的数组,这样程序在遍历的时候便不会出错读者可以练习一下这个过程,思考一下你是如何进行全排列的,当然,你的方法可能和我的不太一样。

我们把上面全排列的方法归纳一下,基本上就是:任意选一个数(一般从小到大或者从左到右)打头,对后面的n-1个数进行全排列。聪明的读者应该已经发现,这是一个递归的方法,因为要得到n-1个数的全排列,我们又要先去得到n-2个数的全排列,而出口是只有1个数的全排列,因为它只有1种,为它的本身。写成比较规范的流程:

1.开始for循环。

2.改变第一个元素为原始数组的第一个元素(什么都没做)。

3.求第2个元素到第n个元素的全排列。

4.要求第2个元素到第n个元素的全排列,要递归的求第3个元素到第n个元素的全排列。

......

5.直到递归到第n个元素到第n元素的全排列,递归出口。

6.将改变的数组变回。

7.改变第一个元素为原始数组的第二个元素。

(注:理论上来说第二次排列时才改变了第一个元素,即第6步应该此时才开始执行,但由于多执行一次无义的交换影响不大,而这样使得算法没有特殊情况,更容易读懂,如果一定要省时间可以把这步写在此处,这种算法我在下文中便不给出了,读者可以自己写。)

 

5.求第2个元素到第n个元素的全排列。

6.要求第2个元素到第n个元素的全排列,要递归的求第3个元素到第n个元素的全排列。

......

5.直到递归到第n个元素到第n元素的全排列,递归出口。

6.将改变的数组变回。

......

8.不断地改变第一个元素,直至n次使for循环中止。

为了实现上述过程,我们要先得到从第m个元素到第n个元素的排列的算法:

从第m个元素到第n个元素的全排列的算法:

public class Test {

    public static void main(String[] string) {

        int[] A = { 1, 2, 3 };
        Full_Array(A, 3);

    }

    static void Full_Array(int A[], int n) {
        Permutation2(A, 0, n);
    }

    static void swap(int[] a, int m, int i) {
        int temp = a[m];
        a[m] = a[i];
        a[i] = temp;

    }

    static void Permutation2(int a[], int m, int n) {

        Set set = new HashSet();
        if (m == n) {
            // Print(A); //直接输出,因为前n-1个数已经确定,递归到只有1个数。
            System.out.println(a);
            return;
        } else {
            for (int i = m; i < n; i++) // 进入for循环,对应第一步
            {
                swap(a, m, i); // 交换,对应第二步
                Permutation(a, m + 1, n); // 递归调用,对应三至五步
                // swap(a, m, i); //交换,对应第六步

            }
        }
    }
}