算法设计与分析-递归算法总结

一、递归的思想和定义

(一)、孙子兵法道:凡治众如治寡,分数是也。在使用计算机解决问题时我们经常遇到的问题是规模较大的问题,不能直接求解,最普遍的方法就是分解问题。递归就是一种特殊的分治思想,在解决一个规模为n的大问题时,先将这个大问题分解为规模为k的与原问题在形式上相同且相互独立的子问题,若子问题不够小继续分解直到子问题小到能直接解决为至,然后解决各个子问题,自底向上的合并各个子问题的解形成原问题的解。



(二)、直接或间接地调用自身的算法称为递归算法 ,用函数自身给出定义的函数称为递归函数 。 


二、常见的递归问题


1、n的阶乘



#include <stdio.h>
//计算n! 
double factorial(double n)
{
	//边界条件
	if(n==1)
		return 1;
	//递归方程
	else
		return n*factorial(n-1);
}

int main()
{
	int n;
	printf("please input n\n");
	scanf("%d",&n);
	printf("%d! =%.2f\n",n,factorial(n));
	return 0;
}


2、Fibonacci数列

无穷数列1,1,2,3,5,8,13,21,34,55, … ,被称为Fibonacci数列。它可以递归地定义为: 



#include <stdio.h>

//计算斐波那契数列的第n项 
double Fibonacci(int n){
	//边界条件
	if(n<=2)
		return 1;
	//递归方程
	else
		return Fibonacci(n-1)+Fibonacci(n-2);
}

int main(){
	int n;
	printf("please input n \n"); 
	scanf("%d",&n);
	printf("Fibbnicc(%d) = %.2f\n",n,Fibonacci(n));
	return 0;
}

3、集合元素的全排列


#include <stdio.h>
#define Length 5

//输出R中所有元素
void printAllElement(int* R){
	int i;
	for(i = 0; i < Length; i++){
		printf("%d ",R[i]);
	}
	printf("\n");
}
//交换R[i],R[j]
void swap(int R[],int i, int j){
	int temp;
	temp = R[i];
	R[i] = R[j];
	R[j] = temp;
}

//递归排列 R[k]--R[m]中的(m-k+1)个元素 
void perm(int* R,int start,int finish){
	//边界条件(R中只有一个元素)
	if(start == finish){
		printAllElement(R);
	}
	//递归方程
	else{
		//循环指定R中的每个元素ri
		for(int i = start; i < finish; i++){
			//通过交换R[i]/R[start]实现指定R[i]到首位置 
			swap(R,start,i);
			//对R中剩下的元素R(start+1,finish)进行排列
			perm(R,start+1,finish);
			//换回原位置
			swap(R,start,i);
		}
	}
}

int main(){
	int R[]={1,2,3,4,5};
	perm(R,0,5);
	return 0;
}


4、Hanoi塔问题


设有A、B、C三个塔座,起始时A上有n个圆盘(编号:1,2,3,...n)自项向下,由小到大排列。现要求把A上的n个圆盘移到到B上,并按原次序排列,移到过程中遵守以下三个规则:

  • 规则一:每次只能移动1个圆盘;
  • 规则二:任何时候都不允许大的圆盘压住小的圆盘;
  • 规则三:在满足规则一、二的情况下可以移动到A、B、C任何一个塔座上。

void Hanoi(int n, int A, int B, int C)
{
	if(n>0)
	{
		//先把A上的前n-1个借助B移动到C上
		Hanoi(n-1,A,C,B);
		//把A上的最下面的第n个移动到B
		move(A,B);
		//最后把C上的n-1个借助A移动到B上
		Hanoi(n-1,C,B,A);	
	}
}


5、正整数的划分

 将正整数n表示成一系列正整数之和:n=n1 +n2+ … +nk ,其中n1≥n2≥ … ≥nk≥1,k≥1,这种表示称为正整数n的划分。求正整数n的不同划分个数。 




#include <stdio.h>

//递归求解正整数n的划分数p(n)=q(n,n) 
int qIntegerDivision(int n,int m){
	
	//边界条件 q(1,1)=1, (n=1,m=1) 
	if(n == 1 && m == 1){
		return 1;
	}
	
	//递归方程 q(n,n), (n<m) 
	if(n < m){
		return qIntegerDivision(n,n);
	}
	
	//递归方程 1+q(n,n-1), (n=m) 
	if(n == m){
		return 1 + qIntegerDivision(n,n-1);
	}
	
	//递归方程 q(n,m-1) + q(n-m,m), (n>m>1) 
	if(n > m && m > 1){
		return qIntegerDivision(n,m-1) + qIntegerDivision(n-m,m);
	}
}

int main(){
	int n,m;
	printf("please input n,m\n");
	scanf("%d,%d",&n,&m);
	printf("q(%d,%d)=%d\n",n,m,qIntegerDivision(n,m));
	return 0;
}

6、Ackerman函数

当一个函数及其它的变量是由函数自身定义时,称这个函数是双递归函数。


#include <stdio.h>

//Ackerman双递归函数 
int ackerman(int n,int m){
	
	//边界条件 A(1,0)=2 
	if(n == 0 && m == 1){
		return 0;
	}
	
	//边界条件 A(0,m)=1, m>=0 
	if(n == 0 && m >= 0){
		return 1;
	}
	
	//边界条件 A(n,0)=n+2, n>=2 
	if(n >= 2 && m == 0){
		return n + 2;
	}
	
	//递归方程 A(n,m)=A(A(n-1,m),m-1), n,m>=1 
	if(n >= 1 && m >= 1){
		return ackerman(ackerman(n-1,m),m-1);
	}
}

int main(){
	int n,m;
	printf("please input n,m\n");
	scanf("%d,%d",&n,&m);
	printf("A(%d,%d)=%d\n",n,m,ackerman(n,m));
	return 0;
}


三、递归优缺点

1、优点:结构清晰、设计简单、容易实现,可用数学归纳法来证明它的正确性。

2、缺点:运行效率低,需要耗费大量的CPU时间和内存空间,尤其当递归变量n大到增大到一定程度时,这种低效率更为明显。

  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值