首先什么是递归呢?程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
递归函数,简单点说就是自己调用自己的函数,就成为递归函数;
一、先来一个最简单的试试手(使用递归的方式,依次从1打印到指定的数字n)
一般来说,递归都要有递归出口,就是满足条件是结束,不然就会死循环,导致程序崩掉。
我们来分析一下,这个函数的递归出口就是当数字为1的时候。如果不是1就调用自身n-1,下面看代码实现。
void func(int n){
if(n == 1){
printf("1\t");
return ;
}else{
func(n-1);
printf("%d\t",n);
}
}
int main(){
func(8);
return 0;
}
2、再来一个简单的例子(斐波那契数列)
这个问题最开始是一个兔子的繁殖问题:
假设有一对兔子,长两个月它们就算长大成年了。然后以后每个月都会生出1对兔子,生下来的兔子也都是长两个月就算成年,然后每个月也都会生出1对兔子了。这里假设兔子不会死,每次都是只生1对兔子。假设兔子不会死。
第一个月,只有1对小兔子;
第二个月,小兔子还没长成年,还是只有1对兔子;
第三个月,兔子长成年了,同时生了1对小兔子,因此有两对兔子;
第四个月,成年兔子又生了1对兔子,加上自己及上月生的小兔子,共有3对兔子;
第五个月,成年兔子又生了1对兔子,第三月生的小兔子现在已经长成年了且生了1对小子,加上本身两只成年兔子及上月生的小兔子,共5对兔子;
这样过了一年之后,会有多少对兔子了呢?这样就构成了一个斐波那契数列;要求n个月之后的兔子对数。
f(1) = 1
f(2) = 1
f(3) = f(1) + f(2)
f(n) = f(n-1) + f(n-2)
最后的代码实现是不是很简单呢。
int Fibonacci(int n){
if(n==1 || n==2)
return 1;
else
return Fibonacci(n-1) + Fibonacci(n-2);
}
int main(){
DWORD start, stop;
start = GetTickCount();
printf("%d\n",Fibonacci(45));
stop = GetTickCount();
printf("time: %lld ms\n", stop - start);
return 0;
}
如果把计算的值改的大一点,你就会发现这样计算会浪费时间
当我计算第45个月有多少的时候,我的电脑就需要差不多6s的时间了
我们可以对上面的程序进行一些优化,可以看到计算的结果是一样的,但是时间还不到1ms,可以节省大量的计算。
int Fibonacci(int n){
static int arr[60] = {0};
if(n==1 || n==2)
return 1;
else{
if(!arr[n-2])
arr[n-2] = Fibonacci(n-2);
if(!arr[n-1])
arr[n-1] = Fibonacci(n-1);
return arr[n-1] + arr[n-2];
}
}
int main(){
DWORD start, stop;
start = GetTickCount();
printf("%d\n",Fibonacci(45));
stop = GetTickCount();
printf("time: %lld ms\n", stop - start);
return 0;
}
3、到这里应该能入门了,写几个简单的测试吧
大家可以看题目,然后看自己能不能写出来
1、计算1+2+3+4+5+。。。。。+n
#include <stdio.h>
//计算1+2+3+4+5+。。。。。+n
int func(int n){
if(n == 1)
return 1;
else
return func(n-1)+n;
}
int main(){
printf("%d\n",func(100));
return 0;
}
2、使用递归计算一个数组的前n项和
#include <stdio.h>
int func(int arr[],int n){
if(n == 1)
return arr[0];
else
return func(arr,n-1)+arr[n-1];
}
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n",func(arr,sizeof(arr)/sizeof(arr[0])));
return 0;
}
3、使用递归求一个数组在L~R之间的和
#include <stdio.h>
int func(int arr[],int L,int R){
if(L == R)
return arr[L];
else
return func(arr,L,R-1)+arr[R];
}
int main(){
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%d\n",func(arr,2,8));
return 0;
}
4、递归求一个数组的最大值和最小值
设置一个查找到范围L R
在L 和R的范围内查找最大值;
递归的出口在L==R的时候
#include <stdio.h>
int func(int arr[],int L,int R){
if(L == R)
return arr[L];
else{
int a = arr[L];
int b = func(arr,L+1,R);
if(a>b)
return a;
else
return b;
}
}
int main(){
int arr[10] = {1,8,9,1,5,8,6,8,4,5};
printf("%d\n",func(arr,2,8));
return 0;
}
5、递归的冒泡排序
#include <stdio.h>
int func(int arr[],int L,int R){
if(L < R){
for(int i = L;i < R-1;i++){
if(arr[i] > arr[i+1]){
arr[i] = arr[i] ^ arr[i+1];
arr[i+1] = arr[i] ^ arr[i+1];
arr[i] = arr[i] ^ arr[i+1];
}
}
func(arr,L,R-1);
}
}
int main(){
int arr[10] = {1,8,9,1,5,8,6,8,4,5};
func(arr,0,10);
for(int i = 0;i < 10;i++){
printf("%d\t",arr[i]);
}
return 0;
}
4、来一个递归的经典案例(汉诺塔)
没玩过的小伙伴,先来看看怎么玩吧
#include <stdio.h>
//n个盘子,ABC分别表示三个柱子
void hanoi(int n,char A,char B,char C){
if(n == 1){
//如果只有一个柱子,直接移动到C柱子
printf("%c-------------->%c\n",A,C);
}
else{
//把剩下的n-1个盘子看做一个整体,从A柱子绕过C柱子,放到B柱子上
hanoi(n-1,A,C,B);
printf("%c-------------->%c\n",A,C);
//在把n-1个盘子从B柱子绕过A柱子放到C柱子上
hanoi(n-1,B,A,C);
}
}
int main(){
hanoi(3,'A','B','C');
return 0;
}