全排列

全排列

//这篇题解是我第一篇题解,很久以前写的,就作为我blog的第一篇文章吧

思路

递归。

N个数的全排列数,观察3个数的全排列数:

1 2 3

1 3 2

2 1 3

2 3 1

3 1 2

3 2 1

可以看到第一个分别是1 2 3,而各自后面所跟着的,恰恰是剩下的数的全排列,以1为例,后面的 2 3 和 3 2是2、3的全排列

又比如4个数全排列:

全排列示意

即先依字典序安排一个数在首位,而剩下的数则在后面进行全排列,这就体现出了递归的思想,由此可见我们可以用递归来处理这个问题

抽象出来就是

Permutation(N){
	Head(); 
	Permutation(N-1); 
}

代码与说明


/*
之前的代码是
void permutation(char *s) {
    if (strlen(s) == 1) {
        printf("%c\n", s[0]);
    }
    else {
        int i;
        for (i = 0; i < strlen(s); i++) {
            printf("%c",s[i]);
            char s1[11] = "";
            char *p = s1;
            for (j = 0; j < strlen(s); j++) {
                if (j != i) { *(p++) = s[j]; }
            }
            permutation(s1);
        }
    }
}
这样是有问题的,不定义before以存储已排列过的部分,则每次递归,都只打印出当前层次未确定的字符的排列。因为对每一层递归来说,上一层递归只执行一次,也就是说这层递归无论有多少情况,上一层都只打印一次。这就造成了缺项。
以 N=3 为例,以 1 为开头的全排列本该有两个,但是会出现 123 和 32 的结果,在第二个结果中1消失了,这是因为在递归中,处理第一位的操作只出现了一次。因此 N=3 的情况下,输出长度呈现 3 2 3 2 3 2 这样的情况。以上图的树来看,四个*的只输出一次,三个*输出4次,两个*输出12次,对应第一位被确定的情况下,第一位被打印出来的次数、第二位被打印出来的次数以及第三位的。
同理,N=4的情况下,输出长度会呈现 4 3 2 2 4 3 2 2 …… 这样的情况。
*/


//以下正文

#include <stdio.h>
#include <string.h>
#include <malloc.h>

/*定义了两个参数,s指的是待全排列的子串,before则保存是已排列过的部分,如1234,1243中的12即是已排列过的部分*/
/*本质上说全排列是位置变动,而与具体的值无关,因此只需要稍微安排一下字符串(为了按字典序输出),就能放心地使用递归直接对子串全排列*/
void permutation(char *s, char *before) {
    if (strlen(s) == 1) {
        int i;
        for (i = 0; i < strlen(before); i++) {
            printf("%c ", before[i]);
        }
        printf("%c\n", s[0]);
    }
    else {
        int i;
        for (i = 0; i < strlen(s); i++) {
            int j;
            char newBefore[11] = "";		
          	/*这里之所以声明一个新的before是为了给每个下级递归分配独立的before避免冲突,事实上直接使用before会导致before一直增加元素直到数组溢出*/
            
          for (j = 0; j < strlen(before); j++) {
                newBefore[j] = before[j];
            }
            newBefore[strlen(newBefore)] = s[i];
            char s1[11] = "";
            char *p = s1;
            for (j = 0; j < strlen(s); j++) {
                if (j != i) { *(p++) = s[j]; }
            }
          	
          	/*对于每一次循环都进行一次递归调用,因为树的每个节点都有相同数量的子树,因此各自递归*/
            permutation(s1, newBefore);
        }
    }
}


int main() {
    char s[11] = "";
    int N;
    scanf("%d", &N);
    int i;
    for (i = 0; i < N; i++) {
        s[i] = (char) (i + '0' + 1);
    }
    char before[11] = "";
    permutation(s, before);

    return 0;
}

posted @ 2018-12-29 13:52  Khunkin  阅读(155)  评论(0编辑  收藏  举报