参考:
3. [算法]列车算法
题目要求:
写一个函数, 如 Foo(const char *str), 打印出 str 的全排列,
如 abc 的全排列: abc, acb, bca, dac, cab, cba
一.全排列的递归实现
为方便起见,用123来示例下。123的全排列有123、132、213、231、312、321这六种。首先考虑123和132如何得出:它们的最高位都固定为1(或者说以1为前缀),后边跟上{2,3}的全排列(至于{2,3}的全排列怎么求,则靠递归来完成);213和231同理:以2为前缀,后边跟上{1,3}的全排列;312和321:以3为前缀,后边跟上{1,2}的全排列。
形式化一点的表述——
令E= {e1 , ..., en }表示n 个元素的集合,我们的目标是生成该集合的所有排列方式。
令Ei 为E中移去元素i 以后所获得的集合,
perm (X) 表示集合X 中元素的排列方式,
ei . p e r m(X)表示在perm (X) 中的每个排列方式的前面均加上ei 以后所得到的排列方式。
举例——
如果E= {a, b, c},那么E1= {b, c},perm (E1 ) = ( b c, c b),e1.perm (E1) = (a b c, a c b)。
对于递归的基本部分,采用n = 1。当只有一个元素时,只可能产生一种排列方式,所以perm (E) = ( e),其中e 是E 中的唯一元素。
当n > 1时,perm (E) = e1.perm (E1 ) +e2.perm(E2 ) +e3.perm (E3) + ⋯ +en .perm (En )。这种递归定义形式是采用n 个perm (X) 来定义perm (E), 其中每个X 包含n- 1个元素。至此,一个完整的递归定义所需要的基本部分和递归部分都已完成。
也就是说——
当n= 3并且E=(a, b, c)时,按照前面的递归定义可得perm (E) =a.perm ( {b, c} ) +b.perm ( {a,c} ) +c.perm ( {a, b} )。
同样,按照递归定义有perm ( {b, c} ) =b.perm ( {c} ) +c.perm ( {b}),
所以a.perm ( {b, c} ) = ab.perm ( {c} ) + ac.perm ( {b}) = a b . c + ac.b = (a b c, a c b)。
同理可得 b.perm ( {a, c}) = ... = (b a c, b c a),c.perm ( {a, b}) = ... = (c a b, c b a)。
所以perm (E) = (a b c, a c b, b a c, b c a,c a b, c b a)。
1 public class Permutation { 2 3 // 生成arr[k:m]的所有排列方式 4 private static void perm(char[] arr, int k, int m) { 5 if (k == m) // 得到了一个排列方式 6 System.out.println(Arrays.toString(arr)); 7 else { 8 // arr[k:m]有多种排列方式,递归地求出来 9 for (int i = k; i <= m; i++) { 10 char t = arr[i]; arr[i] = arr[k]; arr[k] = t; 11 perm(arr, k+1, m); 12 t = arr[i]; arr[i] = arr[k]; arr[k] = t; 13 } 14 } 15 } 16 17 private static void perm(char[] arr) { 18 perm(arr, 0, arr.length-1); 19 } 20 21 public static void main(String[] args) { 22 char[] sequence = new String("abcd").toCharArray(); 23 perm(sequence); 24 } 25 26 }
总结:
找到递归关系,把perm(E)问题转化为n个perm(X)问题,其中每个X 包含n- 1个元素。