这里先搞清楚递归的基本思想,递归函数的运行过程,暂且不考虑是否使用递归在效率方面的差异。
题目 1:
数的组合问题。从1,2,…,n中取出m个数,将所有组合按照字典顺序列出。如n=3,m=2时,输出:12 13 23
1 #include <stdio.h> 2 int Function(int n,int m,int a[],int depth,int output[],int size,int x) //n即指有n个数,m为从n个数中选取的个数,a[]为所选取的数组,depth递归进入深度,output答案,size为大小,x为当前扫描到的位置 3 { 4 if(m == 0) 5 { 6 int i; 7 for(i = 0;i < size;i++) 8 { 9 printf("%d ",output[i]); 10 } 11 printf(" "); 12 return 1; 13 }else 14 { 15 int i; 16 for(i = x;i <= n - m;i++) 17 { 18 output[depth] = a[i]; 19 Function(n,m - 1,a,depth + 1,output,size,i + 1); 20 } 21 } 22 } 23 int main () 24 { 25 int output[2]; 26 int a[5] = {1,2,3,4,5}; 27 Function(5,2,a,0,output,2,0); 28 return 0; 29 }
本题还有不完善之处(参数设置,递归方法等),需继续改进。
题目 2:
小猴子第一天摘下若干桃子,当即吃掉一半,又多吃一个.第二天早上又将剩下的桃子吃一半,又多吃一个.以后每天早上吃前一天剩下的一半另一个.到第10天早上猴子想再吃时发现只剩下一个桃子了问第一天猴子共摘多少个桃子?
1 #include <stdio.h> 2 int Peachcount(int day) 3 { 4 if(day == 10) 5 return 1; 6 return (Peachcount(day + 1)+1) * 2; 7 } 8 int main () 9 { 10 int day = 1; 11 printf("第一天的桃子个数:%d ",Peachcount(1)); 12 return 0; 13 }
题目3:
有雌雄一对兔子,假定过两个月便可每个月繁殖雌雄各一的一对小兔子。问过n个月后共有多少对兔子?
1 #include <stdio.h> 2 int Rabbitcount(int month) 3 { 4 if(month == 0 || month == 1) 5 return 1; 6 return Rabbitcount(month - 1) + Rabbitcount(month - 2); 7 } 8 int main () 9 { 10 int month; 11 printf("请输入过了多少个月: "); 12 scanf("%d",&month); 13 printf("兔子的一共的对数:%d",Rabbitcount(month)); 14 return 0; 15 }
题目4:
一个人赶着鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又一只。这样他经过了七个村子后还剩两只鸭子,问他出发时共赶多少只鸭子?经过每个村子卖出多少只鸭子?
1 #include <stdio.h> 2 int Duckcount(int village) 3 { 4 if(village == 7) 5 return 2; 6 // return Duckcount(village - 1) / 2 - 1; 7 return (Duckcount(village + 1) + 1) * 2; 8 } 9 int main () 10 { 11 int village; 12 printf("请输入经过的村庄个数: "); 13 scanf("%d",&village); 14 int i; 15 for(i = village;i >= 0;i--) 16 { 17 printf("经过第%d个村庄鸭子数:%d ",i,Duckcount(i)); 18 } 19 return 0; 20 }
题目5:
著名的菲波拉契(Fibonacci)数列,其第一项为0,第二项为1,从第三项开始,其每一项都是前两项的和。编程求出该数列前N项数据。
1 #include <stdio.h> 2 int Fib(int n) 3 { 4 if(n == 0) 5 return 0; 6 if(n == 1) 7 return 1; 8 return Fib(n-1) + Fib(n-2); 9 } 10 int main () 11 { 12 int n; 13 printf("请输入所求项数: "); 14 scanf("%d",&n); 15 int i; 16 for(i = 0;i <= n;i++) 17 { 18 printf("第%d项:%d ",i,Fib(i)); 19 } 20 return 0; 21 }
题目6:
输入一个数,求这个数的各位数字之和。
1 #include <stdio.h> 2 int Countsum(int num,int n) 3 { 4 int mask = 1; 5 int i; 6 for(i = 0;i < n;i++) 7 { 8 mask = mask * 10; 9 } 10 mask = mask / 10; 11 if(n == 1) 12 return num; 13 return Countsum((num % mask),n -1 ) + num / mask; //因为这一步所以0不会干扰 14 } 15 int main () 16 { 17 int input; 18 int n = 0; //计算位数 19 printf("请输入您的数字: "); 20 scanf("%d",&input); 21 int temp = input; 22 while(temp > 0) 23 { 24 temp = temp / 10; 25 n++; 26 } 27 printf("该数字的各位的和: %d ",Countsum(input,n)); 28 return 0; 29 }
题目7:
角谷定理。输入一个自然数,若为偶数,则把它除以2,若为奇数,则把它乘以3加1。经过如此有限次运算后,总可以得到自然数值1。求经过多少次可得到自然数1。如:输入22,输出
22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
STEP=16
错误代码
1 #include <stdio.h> 2 //重点总结如何将SETP表示出来,为何出错,失误点? 3 void Function(int *step,int n) 4 { 5 if(n == 1) 6 return ; 7 if( n % 2 == 0) 8 { 9 printf("%d ",n / 2); 10 *step++; 11 Function(step,n / 2); 12 }else 13 if(n % 2 != 0) 14 { 15 printf("%d ",3 * n + 1); 16 *step++; 17 Function(step,3 * n + 1); 18 } 19 20 } 21 int main () 22 { 23 int n; 24 int step = 0; 25 printf("请输入您的数字: "); 26 scanf("%d",&n); 27 Function(&step,n); 28 printf("总共的步数: %d",step); 29 return 0; 30 }
正确代码
1 #include <stdio.h> 2 //重点总结如何将SETP表示出来,为何出错,失误点? 3 void Function(int *step,int n) //因为传入的参数需要经过变化所以一定要用指针!! 4 { 5 if(n == 1) 6 { 7 printf("%d",n); 8 return ; 9 } 10 if( n % 2 == 0) 11 { 12 printf("%d ",n); 13 (*step)++; //自加的优先级比*更高所以必须加上括号 14 Function(step,n / 2); 15 }else 16 if(n % 2 != 0) 17 { 18 printf("%d ",n); 19 (*step)++; 20 Function(step,3 * n + 1); 21 } 22 23 } 24 int main () 25 { 26 int n; 27 int step = 0; 28 printf("请输入您的数字: "); 29 scanf("%d",&n); 30 printf("变化过程:"); 31 Function(&step,n); 32 printf(" "); 33 printf("总共的步数:%d",step); 34 return 0; 35 }
题目8:
梯有N阶,上楼可以一步上一阶,也可以一次上二阶。编一个程序,计算共有多少种不同的走法。
1 #include <stdio.h> 2 int Function(int step,int *pathsum) 3 { 4 if(step < 0) 5 { 6 return *pathsum; 7 }else 8 if(step == 0) 9 { 10 (*pathsum)++; 11 return *pathsum; 12 }else 13 Function(step - 1,pathsum); 14 Function(step - 2,pathsum); 15 return *pathsum; 16 } 17 int main () 18 { 19 int pathsum = 0; 20 int step; 21 printf("请输入一共有的楼梯阶数: "); 22 scanf("%d",&step); 23 printf("共有的走法种数: "); 24 printf("%d",Function(step,&pathsum)); 25 return 0; 26 }
首先想到的方法一:
设S(n) 为阶梯数量,可以容易推出:S(n - 1) = S(n) - 1;或者 S(n - 2) = S(n) - 2; 基准情况: S(0) 和 S(<0)
从输入的阶梯数量n开始,每次调用函数都会让阶梯数 - 1或者 - 2,当阶梯数减到基准情况S = 0 时,说明这一递归过程正确(正好走完阶梯),将路线数pathsum + 1并返回。
当s < 0 时;说明这一递归路线错误(走多了楼梯)将路线数pathsum 返回,不对其改动。
搜索相关资料后,方法2:斐波那契的应用
题目9:
某人写了n封信和n个信封,如果所有的信都装错了信封。求所有的信都装错信封共有多少种不同情况?
题目10:
给定一个整数n,输出这个整数拆分的可能总数
1 #include <stdio.h> 2 int q(int n,int m) //n为要拆分的数字,m为所拆部分中最大值 3 { 4 if(n == 1|| m == 1) 5 { 6 return 1; 7 }else 8 if(n < m) 9 { 10 return q(n,n); 11 }else 12 if(n == m) 13 { 14 return 1 + q(n,n - 1); 15 }else 16 if(n > m) 17 { 18 return q(n,m-1)+q(n - m,m); 19 } 20 } 21 int main () 22 { 23 printf("请输入要拆分的数字 "); 24 int n; 25 scanf("%d",&n); 26 int ret = 0; 27 ret = q(n,n); 28 printf("该数字一共有的拆分方法数 "); 29 printf("%d",ret); 30 return 0; 31 }
此题一开始没有思路,首先找到了 n = 1 时的方法总数,接着思考 q(n)与q(n - 1)的关系难以找到关系,卡住。
查阅网上资料后得到的思路:(http://blog.chinaunix.net/uid-26548237-id-3503956.html)
所谓整数划分,是指把一个正整数n写成如下形式:
n=m1+m2+m3+....+mi;(其中mi为正整数,并且1<=mi<=n),则{m1,m2,m3,....,mi}为n的一个划分。
如果{m1,m2,m3,....,mi}中的最大值不超过m,即max{m1,m2,m3,....,mi} <= m,则称它属于n的一个m划分。这里我们记n的m划分的个数为f(n,m);
例如当n=4时,它有5个划分:{4}、{3,1}、{2,2}、{2,1,1}、{1,1,1,1};
注意:4=1+3和4=3+1被认为是同一个划分。
该问题是求出n的所有划分个数,即f(n,n)。下面我们考虑求f(n,m)的方法。
递归法
根据n和m的关系,考虑下面几种情况:
(1)当n=1时,不论m的值为多少(m>0),只有一种划分,即{1};
(2)当m=1时,不论n的值为多少(n>0),只有一种划分,即{1,1,....1,1,1};
(3)当n=m时,根据划分中是否包含n,可以分为两种情况:
(a)划分中包含n的情况,只有一个,即{n};
(b)划分中不包含n的情况,这时划分中最大的数字也一定比n小,即n的所有(n-1)划分;
因此,f(n,n) = 1 + f(n, n - 1)。
(4)当n<m时,由于划分中不可能出现负数,因此就相当于f(n,n);< span="">
(5)当n>m时,根据划分中是否包含m,可以分为两种情况:
(a)划分中包含m的情况,即{m,{x1,x2,x3,...,xi}},其中{x1,x2,x3,...,xi}的和为n-m,可能再次出现m,因此是(n-m)的m划分,因此这种划分个数为f(n-m, m);
(b)划分中不包含m的情况,则划分中所有值都比m小,即n的(m-1)划分,个数为f(n, m - 1);
因此,f(n,m) = f(n - m,m) + f(n, m - 1)。
综合以上各种情况,可以看出,上面的结论具有递归定义的特征,其中(1)和(2)属于回归条件,(3)和(4)属于特殊情况,而情况(5)为通用情况,属于递归的方法,其本质主要是通过减少n或m以达到回归条件,从而解决问题。
其递归表达式如下所示。
题目 11 :数的全排列问题
1 #include <stdio.h> 2 void swap(int *a,int *b) 3 { 4 int temp; 5 temp = *a; 6 *a = *b; 7 *b = temp; 8 } 9 void Permutation(int input[],int size,int depth) 10 { 11 if(depth == size - 1) 12 { 13 int i; 14 for(i = 0;i < size;i ++) 15 { 16 printf("%d ",input[i]); 17 } 18 printf(" "); 19 return ; 20 }else 21 if(depth < size - 1) 22 { 23 int i; 24 for(i = depth;i < size;i++) 25 { 26 swap(&input[depth],&input[i]); 27 Permutation(input,size,depth + 1); 28 swap(&input[depth],&input[i]); 29 } 30 } 31 } 32 int main() 33 { 34 int input[4] = {1,3,5,7}; 35 printf("1,3,5,7全排列: "); 36 Permutation(input,4,0); 37 return 0; 38 }
思路:例 1,2,3 进行全排列 当数据规模为1时 容易得到那一个数即为所求者,若有两个数字,1,2则所求者为1,2或者2,1
可以推断出全排列即为每一位上的数字依次换成后面的数字({1,2,3},第一位1要换成第1,2,3位的数字,第二位要换成第2,3位的数字,第三位换成第三位数字)
注意换完位置后必须要换回原位(如下图右边线路 1,2,3 ->1,3,2返回前要将3,2换回原来的位置1,2,3供上一层依次调用才不出错)
题目12
要求找出具有下列性质数的个数(包含输入的自然数n):
先输入一个自然数n(n<=1000),然后对此自然数按照如下方法进行处理:
1. 不作任何处理;
2. 在它的左边加上一个自然数,但该自然数不能超过原数的一半;
3. 加上数后,继续按此规则进行处理,直到不能再加自然数为止.
输入
一个自然数n
输出
一个数,表示满足条件的数的个数
样例输入
6
样例输出
6
1 #include <stdio.h> 2 int sum = 0; //收集返回的结果 3 int Function (int n) 4 { 5 int mask = 1; 6 int temp; 7 temp = n; 8 while(temp > 0) 9 { 10 mask = mask * 10; 11 temp = temp / 10; 12 } 13 mask = mask / 10; 14 int initialnum; 15 initialnum = n / mask; 16 if(initialnum == 1) 17 return 1; 18 int i; 19 for(i = 1;i <= initialnum / 2;i++) 20 { 21 sum = sum + Function(n + mask * 10 * i); 22 } 23 return sum; 24 } 25 int main () 26 { 27 int n; 28 printf("请输入数字: "); 29 scanf("%d",&n); 30 int ret; 31 ret = Function(n); 32 printf("方法数: %d",ret); 33 return 0; 34 }
此题一开始疑惑函数如何返回,最后解决方案是设sum为全局变量
题目 13
用递归的方法求N个数中最大的数及其位置。
1 #include <stdio.h> 2 int Compare(int a,int b) 3 { 4 if(a >= b) 5 { 6 return a; 7 } 8 return b; 9 } 10 int Findmax(int input[],int size,int depth) 11 { 12 if(depth == size - 1) 13 { 14 return input[depth]; 15 }else 16 return Compare(input[depth],Findmax(input,size,depth + 1)); 17 } 18 int main () 19 { 20 int a[6] = {2,4,6,4,8,7}; 21 int ret; 22 ret = Findmax(a,6,0); 23 printf("%d",ret); 24 return 0; 25 }
对以上各题的反思总结:
遇到问题1.先观察是否能得到问题小规模化时的解决方法。
2.分析问题规模慢慢变大时其解决方法的过程是否相同。
3.分析问题之间的递归关系。(难点在此处,递归关系的发现是关键之处)
4.将1作为边界,列出关系式。编程解决之。