递归的定义
递归(recursion):程序调用自身的一种编程技巧。
函数递归就是把一个大型复杂的程序转换为一个类似的小型简单的程序,这种方法的主要思想就是把大事化小。
递归的两个必要条件
从数列说起
数列是按照一定规律排列的一组数的序列。
比如:1,2,3,4,5…
以我们熟悉的等差数列(1,3,5,7…),他的规律就是后一项比前一项大2,也就是 f(n)-f(n-1)=2;,那么单知道关系式可以求出整个数列吗?答案是否定的,对于该关系式,(2,4,6,8…)同样满足,我们还需要知道他的首项,也就是1。
递归和数列是一样的,他也需要满足两个条件:
1.规律(关系式)
2.出口(首项)
递归和循环
我们知道,所有的循环都是由规律和初值(首项)表示,那么可以说:所有的循环都可以由递归实现。
但是递归不一定都可以由循环实现。
递归的代码实现
1. 求解斐波那契数列
斐波那契数列:指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)
现在我们用刚才讲过的两条规则实现:
1、规律: F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)
**2、接口:**首项(F(1)=1,F(2)=1)
代码实现如下:
#include <stdio.h>
int f(int n)//n个数
{
if (n == 1)//接口
{
return 1;
}
else if (n == 2)//接口
{
return 1;
}
else//规律
{
return (f(n - 1) + f(n - 2));
}
}
int main()
{
int num = f(8);
printf("num=%d\n", num);
return 0;
}
测试结果为 21,完全正确。
2. 求前n项和
求 1+2+3+4+5+6+7+…用递归实现。
依然是找两点:
1、规律: sum(n)=sum(n-1)+n
2、出口: n=1时,sum=1
代码实现:
int sum(int arr[],int n)
{
if (n == 1)//接口
{
return arr[0];
}
else//规律
{
return (sum(arr, n - 1) + arr[n-1]);
}
}
int main()
{
int arr[7] = { 1,4,2,7,8,2,9 };
int s = sum(arr,7);
printf("num=%d\n", s);
return 0;
}
结果sum=33,完全正确。
求最值
比如求最大值,依然是找两点:
1、规律: 最大值为前n-1项的最大值与n比较之后的最大值
也就是:max(arr, n - 1) 与arr[n]进行比较
2、出口: n=1时,max=arr[0]
int max(int arr[], int n)
{
if (n == 1)//接口
{
return arr[0];
}
else//规律
{
return max(arr, n - 1) > arr[n] ? max(arr, n - 1) : arr[n];
}
}
int main()
{
int arr[7] = { 1,4,2,7,8,2,9 };
int s = max(arr,7);
printf("m=%d\n", s);
return 0;
}
测试结果为:m=9,完全正确
用递归模拟实现strlen函数
1、出口: 当传入空字符时候,也就是’\0’,直接返回 0
2、规律: 总长度=指针后移一位之后的长度 +1,直到遇见 ‘\0’
int my_strlen(const char* str)
{
if (*str == '\0')
{
return 0;
}
else
{
return 1 + my_strlen(str + 1);
}
}
int main()
{
char* p = "abcd";
int len = my_strlen(p);
printf("len=%d\n", len);
return 0;
}
测试结果为:len=4,完全正确
汉诺塔
汉诺塔(Tower of Hanoi),又称河内塔,是一个源于印度古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
假设有三根柱子分别为A,B,C,其中A柱上有n个盘子,如果我们要把A最下面的那个盘子拿出来放到C柱上,我们需要把上面(n-1)个盘子借助C柱全部挪到B柱上,然后将A柱上最大的盘子放到C柱,最后再将B柱上的盘子放回到C柱上。
我们先看最简单的情况:
如果A上只有两个
如果A盘子上有多个,那么:
把(n-1)块看成一个整体,经过不断摆放,放在B上,然后:
最后再把B上的(n-1)块看作整体,经过不断摆放,放在C上。
至于那(n-1)个整体怎么移动,不需要考虑,我们只需要知道:
1、出口: 当只有一块时,A->C
2、规律: 当>=2块时:
① (n-1)块A移动到B,绕过C
② A->C
③ (n-1)块B->C
代码实现:
//(n-1)块A->B。绕过C
//A->C
//B->C
void hanoi(int n,char A,char B,char C)//最终目的是:A->C,第一个字母到第三个字母
{
if(n==1)//接口
{
printf("%c -> %c\n", A, C);
}
else//规律
{
hanoi((n - 1), A, C, B);//把上面的(n-1)看作整体,A->B(第一个字母到第三个字母)
printf("%c -> %c\n", A, C);//A->C
hanoi((n - 1), B, A, C);//把上面的(n-1)看作整体,B->C(第一个字母到第三个字母)
}
}
int main()
{
puts("有两块时:");
hanoi(2, 'A', 'B', 'C');
puts("有三块时:");
hanoi(3, 'A', 'B', 'C');
puts("有四块时:");
hanoi(4, 'A', 'B', 'C');
return 0;
}
运行结果如下:
经过测试,结果正确。