全排列解析

今天没什么事,以前在学校一直没弄清楚全排列的实现。花了点时间研究,总算搞清楚了整个过程。基本思路:设R={r1,r2,…,rn}是要进行排列的n个元素,Ri=R-{ri}.集X中元素的全排列记为Perm(X),(ri)Perm(X)表示在全排列Perm(X)的每一个排列前加上前缀ri得到的排列.R的全排列可归纳定义如下:
  当n=1时,Perm(R)={r},r是集合R中唯一的元素.
  当n>1时,Perm(R)由(r1)Perm(R1),(r2)Perm(R2),….(rn)Perm(Rn)构成
代码如下(采用的测试数据为{1, 2, 3, 4}):

#include <stdio.h>
#include <stdlib.h>

void perm(int a[], int start_pos, int end_pos);
void swap(int *start, int *internal);

int main(void)
{
 int a[4] = {1, 2, 3, 4};
 int start=0, end=0;
 
 perm(a, 0, 3);
 
 return 0;
}

void perm(int a[], int start_pos, int end_pos)
{
 int i, j;
 
 if (start_pos == end_pos)   /* recursive end */
 {
  for (j=0; j<=end_pos; ++j)   /* print the array… */
  {
   printf(”%5d”, a[j]);
  }
  printf(”\n”);
 }
 
 for (i=start_pos; i<=end_pos; ++i)
 {
  swap(a+start_pos, a+i);
  perm(a, start_pos+1, end_pos);
  swap(a+start_pos, a+i);  /* swap back… */
 }
 
 return ;
}

void swap(int *start, int *internal)
{
 int temp;
 
 temp = *start;
 *start = *internal;
 *internal = temp;
 
 return ;
}
递归其实就是数学归纳法的逆过程,数学归纳法是递推,递归则相反(这点我自己领悟到的,^_^)。证明当n=k-1时命题成立相当于递归过程的一个调用,递归终点也就相当于归纳法中的当n=1时命题成立。好了,现在把问题放在全排列上,其实大概的过程我们都可以自己拿支笔演算出来,我们把问题放在程序的核心部分: for (i=start_pos; i<=end_pos; ++i)
 {
  swap(a+start_pos, a+i);   /* 交换1 */
  perm(a, start_pos+1, end_pos);
  swap(a+start_pos, a+i);  /* swap back… */  /* 交换2 */
 }为什么要交换回去,交换一次不就可以了?我把交换2注释掉把程序跑了一遍,出现重复现象,以下是测试结果:   

    1    2    3    4

    1    2    4    3

    1    4    2    3

    1    4    3    2

    1    2    3    4

    1    2    4    3

    2    1    4    3

    2    1    3    4

    2    3    1    4

    2    3    4    1

    2    1    4    3

    2    1    3    4

    3    1    2    4

    3    1    4    2

    3    4    1    2

    3    4    2    1

    3    1    2    4

    3    1    4    2

    2    1    4    3

    2    1    3    4

    2    3    1    4

    2    3    4    1

    2    1    4    3

    2    1    3    4

 出现了重复现象,追其原因,递归到终点打印出了排列序列以后,返回到上一级调用。当r1=1,R1={2, 3, 4}时,函数调用本身perm(R1),直接写出排序吧,2, {3, 4}…2, {4, 3},打印出这两个排列({1, 2, 3, 4}跟{1, 2, 4, 3})后,返回到上一级调用,由于我们把交换2去掉了,所以这时内存中该数组的排列是{1, 2, 4, 3},这个不会随着函数返回而改变,下一个循环,这时该交换2跟4了,刚刚交换的是2跟自己本身,接下来看排列,4, {2, 3}…4, {3, 2},打印出这两个排序({1, 4, 2, 3}跟{1, 4, 3, 2})后,返回到上一级调用,这时内存中该数据的排列是{1,4,3,2},这时接着一下个循环,末尾2跟4交换,变成2, {3, 4}跟2, {4, 3},打印出排列,分别是{1, 2, 3, 4}跟{1, 2, 4, 3},跟前面重复了,而避免出现这种情况的方法就是交换2需要加到程序里面,把当时交换的数据重新交换回去,这样后面的排列可以保证不出现重复现象,这里就不把运行过程重复了,就按照前面的方法分析就可以。所以很多时候,我们多点耐心,仔细地分析程序,收获会很大。

来说两句吧

在评论中,你可以使用以下标签: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>