递归思想博大而精深,巧妙而精简,需要慢慢体会.
知识点小结:
问题经过泛化之后,定义一个函数包装器,这个包装器是相对规范的,暂且叫它递归模型,它由两部分组成,一是递归出口,即问题的最简单情景,二是递归体,它是用来解决子问题的,使子问题向最简单情况转化,注意一定是使问题越来越简单。
代码实例1:生成费波那契序列
1 /* fibonacci.c --- 2 * 3 * Filename: fibonacci.c 4 * Description: 费波那契序列 5 * Author: magc 6 * Maintainer: 7 * Created: 四 8月 2 09:35:28 2012 (+0800) 8 * Version: 9 * Last-Updated: 四 8月 2 12:04:49 2012 (+0800) 10 * By: magc 11 * Update #: 75 12 * URL: 13 * Keywords: 14 * Compatibility: 15 * 16 */ 17 18 /* Commentary: 19 * 费波那契序列:1,1,2,3,5,8,13,21.... 20 * 形成递归的子问题就是根据前两项之和得出当前值, 21 * 22 */ 23 24 /* Change Log: 25 * 26 * 27 */ 28 29 /* Code: */ 30 31 #include <assert.h> 32 #include <ctype.h> 33 #include <errno.h> 34 #include <limits.h> 35 #include <string.h> 36 #include <stdarg.h> 37 #include <stdlib.h> 38 #include <stdio.h> 39 40 void fiboSeqence(int *seq , int len); 41 void outputSeq(int *seq , int len); 42 43 44 int main(int argc, char * argv[]) 45 { 46 printf("请输入一个整数n,将输出费波那契前n项:\n"); 47 int n; 48 int res = scanf("%d",&n); 49 int *seq = malloc(sizeof(int) * n); 50 if(seq == NULL)error("no memory available!\n"); 51 52 fiboSeqence(seq,n); 53 outputSeq(seq,n); 54 free(seq); 55 } 56 /************************************************************************* 57 *功能描述:输出序列的内容 58 *参数列表: 59 *返回类型: 60 **************************************************************************/ 61 void outputSeq(int *seq , int len) 62 { 63 int i; 64 for (i = 0; i < len; i++) { 65 printf("%d,",seq[i]); 66 } 67 printf("\n"); 68 } 69 /************************************************************************* 70 *功能描述:形成递归的子问题是根据前面两项值之和确定当前值,此函数的使命就是根据这个关系,确定第len个的值。 71 *参数列表: 72 *返回类型: 73 **************************************************************************/ 74 void fiboSeqence(int *seq , int len) 75 { 76 //鉴于此序列前两项特殊,从第三项开始才有规律,所以将前两项作为递归出口 77 if(len == 1) //递归出口 78 { 79 seq[0] = 1; 80 return; 81 } 82 if(len == 2)//递归出口 83 { 84 seq[0] = 1; 85 seq[1] = 1; 86 return; 87 } 88 fiboSeqence(seq ,len - 1 );//若得到第len个值,就先得到第len-1个值 89 seq[len-1] = seq[len-2] + seq[len - 3];//解决递归子问题:第len个值是由它前两项之和得出,它依赖于先确定它前面的值,所以得放到递归调用后面。 90 } 91 92 93 /* fibonacci.c ends here */
注 :
1)分析费波那契序列的规律,可以看出从第三项开始具有的规律,形成递归的子问题就是当前值是由其前面两项之和得出,
2)通过一个动态数组来存放此序列。
3)递归结束的出口就是此序列的前两项
在GCC编译运行结果是:
代码示例2:汉诺塔经典问题
1 /* hanota.c --- 2 * 3 * Filename: hanota.c 4 * Description: 汉诺塔 5 * Author: magc 6 * Maintainer: 7 * Created: 五 8月 3 10:34:43 2012 (+0800) 8 * Version: 9 * Last-Updated: 五 8月 3 14:47:24 2012 (+0800) 10 * By: magc 11 * Update #: 72 12 * URL: 13 * Keywords: 14 * Compatibility: 15 * 16 */ 17 18 /* Commentary: 19 * 汉诺塔经典问题 20 * 有三个圆柱A,B,C,将A柱子上的n个下大上小依次叠放的圆盘移动到B上, 21 * 移动规则:一次只移动一个盘子,且保证每个柱子上的盘子都是下边大,上面小的顺序 22 * 最简单的情况是A上只剩下一个,直接从A移动到B上。 23 * 分析问题,例如有8个盘子,若将8个盘子从A到B,问题分解为将A上面7个盘子移到C作为临时存放,将A上第8个移动到B,再将C上7个移动到B。 24 * 将7个盘子从C到B问题又可分解为,将上面6个盘子移动到A上临时存放,将第七个盘子从C到B,以此类推 25 * 符合将问题分析为更简单,并且具有相同特点的问题。 26 * 27 */ 28 29 /* Change Log: 30 * 31 * 32 */ 33 34 /* Code: */ 35 #include <assert.h> 36 #include <ctype.h> 37 #include <errno.h> 38 #include <limits.h> 39 #include <string.h> 40 #include <stdarg.h> 41 #include <stdlib.h> 42 #include <stdio.h> 43 44 void hanota(int n,char src,char dest,char temp); 45 void moveTower(char src,char dest); 46 47 int main(int argc, char * argv[]) 48 { 49 //为三个圆柱编号 50 char A = 'A',B = 'B',C = 'C'; 51 hanota(3,A,B,C); 52 53 //目标是将A上的8个圆盘移到B上去 54 55 } 56 /************************************************************************* 57 *功能描述:移动汉诺塔上的盘, 58 *参数列表:n代表共几个盘子,src代表源位置,dest代表要移动的目标柱子,temp代表作为临时存放的第三个柱子 59 *返回类型: 60 **************************************************************************/ 61 void hanota(int n,char src,char dest,char temp){ 62 63 if(n == 1){ //最简单情况,只有一个时,直接移动到目标柱子上 64 moveTower(src,dest); 65 } 66 else{ 67 hanota(n-1,src,temp,dest);//第一步:将上面n-1个盘子移动到临时柱子上 68 moveTower(src,dest); //第二步:将第n个盘子移动目标柱子上 69 hanota(n-1,temp,dest,src);//第三步:将临时柱子上的移动到目标柱子上 70 } 71 72 } 73 74 /************************************************************************* 75 *功能描述:将某个圆盘从一个柱子上移到另一个柱子上 76 *参数列表: 77 *返回类型: 78 **************************************************************************/ 79 void moveTower(char src,char dest){ 80 printf("由%c移动到%c\n",src,dest); 81 82 } 83 84 85 86 87 88 /* hanota.c ends here */
注:
1)体会递归问题的分解过程,问题泛化,得到函数包装器hanota,用来解决子问题,通过最简单情况作为递归的出口。
2)注意分析问题的过程,把握实质,如三个柱子,有多少个盘子需要表示,而每个盘子编号并不重要的,
在GCC下编译运行的结果是:
3、Scrable拼字游戏,要求将一组字母重新排列组成一个单词的能力,输出所有可能的字母组合