所谓排列,就是从n个不同的元素中,任取m(m <= n)个排成一列。例如,从[1, 2, 3]中任取2个元素进行排列,就有以下6种情况:[1, 2], [1, 3], [2, 1], [2, 3],[3, 1], [3, 2]。

我的想法是从一维数组a中任取m个元素进行排列,把结果保存在二维数组result中(简单起见,所有元素均为int型别)。二维数组的列数自然是m,我们需要知道二维数组的行数,也就是从一维数组中任取m个元素的排列数。

首先,我们写一个方法,这个方法返回从n个不同元素中任取m个元素的排列数(记作A(n, m))。假设有m个空,我们要把n个不同的数填进去,那么第1个空有n种不同的填法,之后第2个空有n - 1种不同的填法,最后要填第m个空时,还有n - (m - 1)个数,所以第m个空有n - m + 1种不同的填法,由此可以得到:

A(n, m) = n * (n - 1) * (n - 2) * ... * (n - m + 1)

代码如下:

    /**

     * 使用填空法计算从n个不同元素中任取m个元素的排列数

     * @param n 元素总数,需满足 n > 0

     * @param m 要选出做排列的元素数,需满足 0 < m <= n

     * @return 从n个不同元素中任取m个元素的排列数

     * @throws ArithmeticException if (n <= 0 || m <= 0 || m > n)

     */

   public static int numberOfPermutation(int n, int m) {

       if (n <= 0 || m <= 0 || m > n) {

           throw new ArithmeticException("从n个不同元素中任取m个元素进行排列,"

                   + "n, m应满足如下条件:"

                   + "n > 0 且 0 < m <= n");

       }

 

        int result = 1;

        for (int i = n; i > n - m; i--) {

            result *= i;

        }

 

        return result;

    }

然后,我们重载上面那个方法:

    /**

     * 使用填空法计算数组a中任取m个元素的排列数

     * @param a 数组,不能为空

     * @param m 要选出做排列的元素数,需满足 0 < m <= a.length

     * @return 从n个不同元素中任取m个元素的排列数

     * @throws ArithmeticException if (a.length == 0 || m <= 0 || m > a.length)

     */

   public static int numberOfPermutation(int[] a, int m) {

       if (a.length == 0 || m <= 0 || m > a.length) {

           throw new ArithmeticException("从数组a任取m个元素进行排列,"

                   + "a, m应满足如下条件:"

                   + "a.length > 0 且 0 < m <= a.length");

       }

       return numberOfPermutation(a.length, m);

   }

这个方法接收一个数组,返回的是从中取出m个元素的排列数,

这样存放结果的二维数组就可以这样定义:

int[][] result = new int[numberOfPermutation(a, m)][m];

下面我们来看从[1, 2, 3]中任取2个元素进行排列的过程。

首先,我们从[1, 2, 3]中取出1,把它放入result的第1列,原数组变成了[2, 3]。

从[2, 3]中任取1个元素进行排列,把结果保存在二维数组temp中。只需把2和3分别取出放在temp第1行和第2行的第1列。

之后,把temp追加到result的第2列。

从[2, 3]中任取1个元素的排列有2种,所以我们就得到了result的第1行和第2行。

重复上述过程,即可完成排序。

整个过程中,result的变化简单表示如下:

0   0 -> 1 0 -> 1 2 -> 1 2 -> 1 2 -> 1 2 -> 1 2

0 0 -> 1 0 -> 1 3 -> 1 3 -> 1 3 -> 1 3 -> 1 2

0 0 -> 0 0 -> 0 0 -> 2 0 -> 2 1 -> 2 1 -> 2 1

0 0 -> 0 0 -> 0 0 -> 2 0 -> 2 3 -> 2 3 -> 2 3

0 0 -> 0 0 -> 0 0 -> 0 0 -> 0 0 -> 3 0 -> 3 1

0 0 -> 0 0 -> 0 0 -> 0 0 -> 0 0 -> 3 0 -> 3 2

代码如下:

    public static int[][] permutation(int[] a, int m) {

        int[][] result = new int[numberOfPermutation(a, m)][m];

// 如果m为1,result只有1列

// 只需把a中元素依次取出,放入result

        if (m == 1) {

            for (int j = 0; j < a.length; j++) {

                result[j][0] = a[j];

            }

            return result;

        }

 

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

// 从a中删除a[i],把剩余元素放入临时数组

// 方法int[] delete(int[] a, int i)删除数组a中下标为i的元素,

// 并产生一个新数组返回,事实上它并没有对数组a做出更改

            int[] temp = delete(a, i);

// 计算从temp中任取m - 1个元素的排列数t

            int t = numberOfPermutation(temp, m - 1);

// 外层循环每进行1次,都将填充result的第i * t行至第(i + 1) * t行的第1列

            for (int j = 0; j < t; j++) {

                result[i * t + j][0] = a[i];

            }

// 第i * t行至第(i + 1) * t行的剩余列,

// 使用从temp中任取m - 1个元素的排列结果进行填充

// 方法void matrixCopy(int[][] src, int srcRowPoc, 

// int srcColPoc, int[][] dest, int destRowPoc, int destColPoc)

// 从二维数组src中第srcRowPoc行第srcColPoc列开始,

// 将剩余元素复制到二维数组dest中,

// dest从第destRowPoc行第destColPoc列开始接收数据

            matrixCopy(permutation(temp, m - 1), 0, 0,  result, i * t, 1);

        }

        return result;

    }

关于方法

int[] delete(int[] a, int i) 和

void matrixCopy(int[][] src, int srcRowPoc, int srcColPoc, int[][] dest, int destRowPoc, int destColPoc),明天再说,今天已经很晚了。