目录
一、递归概述
(1)分治算法策略主要通过递归实现,大规模问题分解成小规模问题
可以理解为分治是一种算法思想,递归是实现这种思想的一种手段
- 分治法的设计思想:将一个难以直接解决的大问题,分解成一些规模较小的相同问题,以便各个击破,分而治之
- 直接或间接地调用自身的算法/函数称为递归算法/函数
- 用递归解决问题时,关键是确定递归体和递归出口
(2)掌握用递归方法解决问题:n!、菲波那契数列、汉诺塔问题、猴子摘桃问题、十进制转换为二进制、逆序(或正序)输出一个正数中的每一位数、集合的全排列、整数划分问题、递归求平方和函数(openjudge题目)
(3)递归程序的优点:
在计算机算法设计与分析中,使用递归技术往往使函数的定义和算法的描述简洁且易于理解。
(4)递归程序的缺点:
递归算法解题的运行效率较低
在递归调用过程中,系统为每一层的返回点、局部变量等开辟了堆栈来存储。递归次数过多容易造成堆栈溢出等。
(5)解题思路:求n!
- 递推:递推法的特点是从一个已知的事实(如1!=1)出发,按一定规律推出下一个事实(如2!=1!*2),再从这个新的已知的事实出发,再向下推出一个新的事实(3!=3*2!)。n!=n*(n-1)!。
- 递归:即5!等于4!×5,而4!=3!×4…,1!=1
其实这样感觉的话递归、递推也没有太大的区别,那就从题目代码上来看一下区别吧。
一、求n!
#include <iostream>
using namespace std;
int fac(int n)//n>=0
{
int f;
if(n==0||n==1)
f=1;
else
f=fac(n-1)*n;
return f;
}
int main(){
int n;//代表有几列
cin>>n;
cout<<fac(n)<<endl;
}
二、斐波那契数列
用递推输出的是n个斐波那契数列
用递归输出的是第n个斐波那契数列是多少?
#include <iostream>
using namespace std;
int fib(int n)//n>=0
{
int a;
if(n==1||n==2)
a=1;
else
a=fib(n-1)+fib(n-2);
return a;
}
int main(){
int n;//代表有几列
cin>>n;
cout<<fib(n)<<endl;
}
三、汉诺塔问题
用递推求的是移动n个盘需要多少次?
用递归求的是如何移动?
#include <iostream>
using namespace std;
void move(char from,char to)
{
cout<<"Move "<<from<<" to "<<to<<endl;
}
void hanoi(int n,char first,char second,char third)
{
if(n==1)
move(first,third);//递归出口
else{
hanoi(n-1,first,third,second);//A->B
move(first,third);
hanoi(n-1,second,first,third);//B->C
}
}
int main(){
int m;
cout<<"小圆盘的个数为:";
cin>>m;//m个小圆盘需要移动
hanoi(m,'A','B','C');
}
四、猴子摘桃问题
递推关系:f(n-1)=(f(n)+1)*2 边界条件:f(10)=1
递归·终止条件 n=10,返回1;
#include <iostream>
using namespace std;
int f(int n)//n>=0
{
if(n==10)
return 1;
else
return (f(n+1)+1)*2;
}
int main(){
cout<<f(1)<<endl;
}
五、十进制转换为二进制
编写一个递归函数,将10进制转化成radix进制(输出二进制形式)
【除2求余,到商为0终止】
#include <iostream>
using namespace std;
int change(int a,int radix)
{
if(a!=0)
{
change(a/radix,radix);
cout<<a%radix;//输出余数
}
}
int main(){
int x,radix;
//x是十进制数,radix是基数(转换为radix进制)
cin>>x>>radix;
change(x,radix);
cout<<endl;
return 0;
}
六、逆序(或正序)输出一个正数中的每一位数
例如,对于数12345,依次输出5 4 3 2 1
#include <iostream>
using namespace std;
int change(int a)
{
if(a/10==0)//个位数
{
cout<<a;
}
else //正序输出只需要交换这两句的位置即可;
{
cout<<a%10<<" ";//输出余数
change(a/10);
}
}
int main(){
int x;
cin>>x;
change(x);
cout<<endl;
return 0;
}
七、集合的全排列【一点点不好理解】
#include <iostream>
using namespace std;
int Perm(int list[],int k,int m)
{
if(k==m)
{
for(int i=0;i<=m;i++)
cout<<list[i]<<" ";
cout<<endl;
}
else
{
for(int j=k;j<=m;j++)
{
swap(list[k],list[j]);
Perm(list,k+1,m);
swap(list[k],list[j]);
}
}
}
int main(){
int a[4]={1,2,3,4};//对集合a中的4个元素进行全排列
Perm(a,0,3);//0代表第一个数是第几个位置,3代表最后一个数是第几个位置
return 0;
}
八、整数划分问题
将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,
其中n1≥n2≥…≥nk≥1,k≥1。
正整数n的这种表示称为正整数n的划分。求正整数n的不同划分个数。
分析:
整数6的划分方法数 =最大加数等于6的划分方法数 +最大加数等于5的划分方法数 +最大加数等于4的划分方法数 +最大加数等于3的划分方法数 +最大加数等于2的划分方法数 +
最大加数等于1的划分方法数
即:
整数6的划分方法数 =最大加数等于6的划分方法数 +最大加数不大于5的划分方法数
或者
整数6的划分方法数 =最大加数不大于6的划分方法数
将n的最大加数不大于m的划分个数记作q(n,m)
- n>m>1 q(n,m)=q(n,m-1)+q(n-m,m)
- n=1||m=1 q(n,m)=1
- m>n q(n,m)=q(n,n)
- m=n q(n,m)=q(n,m-1)+q(n-m,m)
#include<iostream>
using namespace std;
int zshf(int n,int m){
if(n<1)
return 0;
else if(n==1||m==1)
return 1;
else if(n<m)
return zshf(n,n);
else if(n==m)
return zshf(n,n-1)+1;
else
return zshf(n,m-1)+zshf(n-m,m);
}
int main(){
int n;
n=zshf(6,3);//6的最大加数不大于3的划分个数
cout<<n<<endl;
return 0;
}
九、递归求平方和函数(openjudge题目)
#include<iostream>
using namespace std;
int f(int n)
{
if(n==0||n==1)
return n;
else
return n*n+f(n-1);
}
int main(){
int n;
cin>>n;
cout<<f(n)<<endl;
}
大家都要加油啊!!!