问题描述:
1 . 递归和非递归分别实现求第n个斐波那契数 1、1、2、3、5、8、13、21、……
2 . 编写一个函数实现n^k,使用递归实现
3 . 写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和,例如,调用DigitSum(1729),则应该返回
1+7+2+9,它的和是19
4 . 编写一个函数reverse_string(char * string)(递归实现)
实现:将参数字符串中的字符反向排列
要求:不能使用C函数库中的字符串操作函数
5 . 递归和非递归分别实现求n的阶乘
6 . 递归方式实现打印一个整数的每一位
1. 斐波那契数的规律是前两个数均是1,从第三个数开始,第三个数及以后的数值均为后两数的和。函数体中主要实现后两数相加赋给第三个数,并且循环这个函数体那么就能得到任意一个斐波那契数。而这个我们要实现循环体的自调用,循环体自己调用自己便是递归!源代码及结果如下:
非递归实现:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int Fib(int n) {
int a = 1;
int b = 1;
int c = 0;
int i = 0;
if (n <= 2) {
return 1;
}
else {
for (i = 0; i < n-2; i++) {
c = a + b;
a = b;
b = c;
}
return c;
}
}
int main()
{
int a = 0;
scanf("%d", &a);
a = Fib(a);
printf("%d\n", a);
system("pause");
return 0;
}
非递归实现较为直观,下来我们来看看递归实现:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int Fib(int n) {
if (n <= 2) { //前两个数均是1
return 1;
}
else {
return Fib(n - 1) + Fib(n - 2); //递归主体
}
}
int main()
{
int a = 0;
scanf("%d", &a);
a = Fib(a);
printf("%d\n", a);
system("pause");
return 0;
}
检查结果是正确的!但是提出几点思考问题,可以试试计算第40、50(不考虑溢出时),它的效率是非常低的,在计算第40个斐波那契数时,我们将其拆分计算38、39,在计算38时将其拆分为37、36,在计算39时拆分为38、37,这样便有:
40
39 38
38 37 37 36
37 36 36 35 36 35 35 34
.................................
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int count = 0; //全局变量
int Fib(int n)
{
if (n == 3) { //计算3被重复计算的次数
count++;
}
if (n <= 2) { //前两个数均是1
return 1;
}
else {
return Fib(n - 1) + Fib(n - 2); //递归主体
}
}
int main()
{
int a = 0;
scanf("%d", &a);
a = Fib(a);
printf("%d\n", a);
printf("重复计算:%d\n", count);
system("pause");
return 0;
}
现在便开始重复了,在计算较大数时,小的数字会计算的越来越多,在计算40是,可以添加计数count打印3被计算的次数竟有3000多万次!!!这个效率是非常低的,相较于非递归时,这个相当的不适用!
递归写出来的表现形式很简单,但是想法思路比较难!并且在此可以看出,非递归更胜一筹,那么何时采用递归、何时采用非递归便是我们需要考虑的!
2. 有了前面对斐波那契数第递归实现热身战,阶乘的递归实现很简单,直接给出源代码及结果如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int M(int i, int k) {
if (k == 0) {
return 1;
}
else {
return i * M(i, k-1);
}
}
int main()
{
int n = 0;
int k = 0;
scanf("%d %d", &n, &k);
printf("%d\n", M(n, k));
system("pause");
return 0;
}
再次领略“递归”的神奇之处,它的语句很简单但很有威力,使函数重复进行自调用。在这,能完美的解决这个问题!
3. 在这道题中要有一种“拆分”的思想,需要将非负两位数以上整数的各个位数的数字进行相加,那么重点自然就转到了怎么求得各个数位上的数字,再相加即可。思考后发现,从低位到高位依次取时:先模10,数字不变,再除10,保留结果再进行模,结果不变,再除10,保留结果再模...当它变成1位数的时候结束!
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int P(int a) {
int sum = 0;
if (a / 10 == 0) {
return a;
}
else {
return sum = a % 10 + P(a / 10); //将1823分解为 182 3
} //将182分解为 18 2
} //将18分解....
int main()
{
int i = 0;
scanf("%d", &i);
printf("%d\n", P(i));
system("pause");
return 0;
}
结果正确,注意理解递归体内的思想,以及多位数的拆分思想!
4. 逆序C风格字符串,练习的已经很多遍了。题目要求参数的指针,我们在此将其传为数组,由第一题知道,这两者完全等价!在函数体内没有办法得到数组元素个数,而C风格字符串可以采用strlen函数完美解决这个问题。大家都知道C风格字符串的特殊之处在于后面多了一个'\0',那么strlen函数会不会也计算它呢?仔细甄别定义!
非递归实现:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void reverse_string(char arr[]) {
char *left = arr;
char *right = arr + strlen(arr) - 1;
while (left < right) {
char tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
int main()
{
char arr[] = "abcdef";
reverse_string(arr);
printf("%s\n", arr);
system("pause");
return 0;
}
递归实现:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void reverse_string(char arr[]) {
int len = strlen(arr);
char tmp = *arr;
*arr = *(arr + len - 1);
*(arr + len - 1) = '\0';
if (strlen(arr + 1) > 1) { //理解
reverse_string(arr + 1);
}
*(arr + len - 1) = tmp;
}
int main()
{
char arr[] = "abcdef";
reverse_string(arr);
printf("%s\n", arr);
system("pause");
return 0;
}
5. 有了前面对众多例题的详解,阶乘的递归实现很简单,直接给出源代码及结果如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int factorial(int i) {
if (i <= 1) {
return 1;
}
else {
return i * factorial(i - 1);
}
}
int main()
{
int a = 0;
scanf("%d", &a);
printf("%d\n", factorial(a));
system("pause");
return 0;
}
6. 在第3题的时候,我们得到了各个位数相加的和,即得到了各个位数的具体数字。经过稍加改动,可以将各个数位的数字分别输出,但是,却是倒序输出。针对这个情况,答案近在咫尺,就是“模”、“除”的顺序关系只需颠倒一下就圆满完成任务了,源代码及结果如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int Print(int a) {
int i = 0;
if (a / 10 == 0) {
return a;
}
else {
i = Print(a / 10);
printf("%d ", i);
return (a % 10);
}
}
int main()
{
int i = 0;
scanf("%d", &i);
printf("%d\n", Print(i));
system("pause");
return 0;
}
结果正确,在此抛出一个问题:如何正确理解递归函数体具体数值变化关系?