一个函数调用其自身就是递归,也是利用栈实现,每调用一次进一次栈
注意找到递归的终止条件
作用
- 替代多重循环
- 解决本来就是用递归形式定义的问题
- 将问题分解规模更小的子问题求解(如求阶乘)
例题讲解
🚩1.阶乘问题
思路讲解:如我们要求3的阶乘,那我们可以把问题分解成求3乘上2的阶乘,然后还可以再把2的阶乘分解成2乘上1的阶乘。
现在我们已知digui()这个函数就是求一个数的阶乘的函数,3的阶乘分解成两个小问题,对于2的阶乘我们直接可以用digui(2)就可以求。
但要注意不能无穷的递归下去,要有一个临界点,到了这个临界点后就可以直接返回不用再接着调用digui()函数了,在这题里,如果要求1的阶乘那么可以直接返回1而不用再接着分解问题了。
作为基础入门,代码如下
int digui(int n)
{
if(n==0)
return 1;
return n*digui(n-1);
}
🚩2.汉诺塔问题(分解成小问题)
有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。
思路讲解:比如我们现在要把3个圆盘从塔1移到塔3,那么一定是A先放到塔3最下面,然后B和C再移到塔3,这样的话就要先把B和C移走。
B和C又不能移到塔3,可以先移到塔2作为中间结点然后再移到塔3。这样要解决3层汉诺塔问题要先解决2层汉诺塔问题即把BC如何移到塔2
B和C要想移到塔2首先C要先移走且不能移到塔2因为塔2必须是最底下的B先移动,那么BC要从塔1移到塔2就要利用塔3当做中间节点
这样两层汉诺塔问题就拆解成了两个1层汉诺塔的问题而1层汉诺塔可以直接移动至此解决
解题步骤:
- 第一步
- 把n-1个圆盘 从塔1移动到塔2
- 第二步
- 把第n个圆盘 从塔1移动到塔3
- 第三步
- 把n-1个圆盘 从塔2移动到塔3
原理:看到这里是不是觉得和阶乘的解决方法类似
- 要想解决n层汉诺塔问题必须解决n-1层汉诺塔问题
- 解决n-1层汉诺塔问题必须解决n-2层汉诺塔问题
- …
- 必须解决1层汉诺塔问题,而1层可以直接移动。类似于阶乘中如果求1的阶乘那么不用再调用函数可以直接返回
🦄代码如下
void Hanoi(int n,char src,char mid,char dest)
{
if(n==1) //递归结束条件
{
cout<<src<< "->" <<dest<<endl; //只有一个盘子时直接移动即可
return;
}
Hanoi(n-1,src,dest,mid); //第一步,把n-1个圆盘 从塔1移动到塔2
cout<<src<< "->" <<dest<<endl; //第二步, 把第n个圆盘 从塔1移动到塔3
Hanoi(n-1,mid,src,dest); //第三步,把n-1个圆盘 从塔2移动到塔3
}
//Hanoi(3,'1','2','3');
//1->3 第一步,2个圆盘从塔1移到塔2
//1->2
//3->2 第一步结束
//1->3 第二步,到这塔1移到了塔3
//2->1 第三步,2个圆盘从塔2移到塔3
//2->3
//1->3 第三步结束
把问题拆解分成几个小步骤,而对于n-1个圆盘的移动直接调用函数改下参数即可
http://player.bilibili.com/player.html?aid=753044588&bvid=BV1Hk4y1k7KL&cid=186093717&page=1
🚩3.[蓝桥杯][算法提高VIP]最大乘积(替代多重循环)
对于n个数,从中取出m个数,如何取使得这m个数的乘积最大呢?
输入
第一行一个数表示数据组数每组输入数据共2行:第1行给出总共的数字的个数n和要取的数的个数m,1<=n<=m<=15,第2行依次给出这n个数,其中每个数字的范围满足:a[i]的绝对值小于等于4。
输出
每组数据输出1行,为最大的乘积。
样例输入
1 5 5 1 2 3 4 2
样例输出
48
思路:用递归代替多重循环(因为不知道有几重)
#include<bits/stdc++.h>
using namespace std;
int a[20];
int vis[20];
int maxn=-1e8;
int n,m;
void dfs(int x,int sum,int step) //x记录起始搜寻位置,sum记录当前乘积,step记录当前找了几个数
{
if(step==m)
{
maxn=max(maxn,sum);
return;
}
for(int i=x;i<n;i++)
{
if(vis[i]==0)
{
vis[i]=1;
dfs(i+1,sum*a[i],step+1);
vis[i]=0;
}
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>m;
for(int i=0;i<n;i++)
cin>>a[i];
dfs(0,1,0);
cout<<maxn<<endl;
maxn=0;
}
return 0;
}
🚩4.N皇后问题(替代多重循环)
在N×N格的国际象棋上摆放N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
思路讲解:可以递归来求解,尝试每种解法,如果都不冲突就输出这种解法
对于第k行的皇后依次遍历所放位置i和前k-1行已经摆好的皇后判断是否冲突。
若在同一列那所放位置和之前某个皇后的列位置相同,若斜线冲突,那两个冲突的皇后行的差值和列的差值的绝对值相同
🦄代码如下(本质上是N重循环只不过用递归代替)
#include<bits/stdc++.h>
using namespace std;
int N;
int queenPos[100]; //第i行的皇后放在哪一列
void NQueen(int k) //在1~k-1行皇后已经摆好的情况下,摆第k行及其后的皇后
{
if(k==N+1) //N个皇后已经摆好,此时输出摆法
{
for (int i = 1; i <= N; i++)
{
cout<<queenPos[i]<<" ";
}
cout<<endl;
return;
}
for (int i = 1; i <= N; i++) //逐个尝试第k行皇后的列位置i
{
int j;
//和已经摆好的前k个皇后的位置比较,看是否冲突
for (j = 1; j < k; j++)
{
if(queenPos[j]==i || abs(queenPos[j]-i)==abs(k-j))
break; //冲突,尝试下一个位置
}
if(j==k) //当前选的列位置i不冲突(只有不冲突上面的for循环才不会break)
{
queenPos[k]=i; //将第k个皇后摆放在列位置i
NQueen(k+1);
}
}
}
int main(){
cin>>N;
NQueen(1);
return 0;
}
🚩5.逆波兰表达式(递归解决递归形式的问题)
如2 + 3用逆波兰表达式为+ 2 3,(2+3)* 4表示为* + 2 3 4,本题求逆波兰表达式的值
输入:* + 11.0 12.0 + 24.0 35.0
输出:1357.000000
提示:(11.0+12.0)*(24.0+35.0)
思路:首先来看逆波兰表达式的定义
- 一个数就是一个逆波兰表达式,值为该数
- "运算符 逆波兰表达式 逆波兰表达式"也是逆波兰表达式
从上面的逆波兰表达式的定义可以看到定义中又出现了逆波兰表达式这不就和递归一样吗,自身又用到了自身
而显然终止条件就是一个数的情况
🦄代码如下
#include<bits/stdc++.h>
using namespace std;
double exp()
{
string s;
cin>>s; //cin遇到空格会停止
switch (s[0])
{
case '+': return exp()+exp();
case '-': return exp()-exp();
case '*': return exp()*exp();
case '/': return exp()/exp();
default: return stod(s); //stod函数将字符串类型转换为double类型
break;
}
}
int main(){
cout<<exp()<<endl;
return 0;
}
// + 2 3
// 首先读入+然后case '+'执行return exp()+exp()
//第一个exp()也会执行cin>>s;这时的s是2直接return stod("2")
//第二个exp()也会执行cin>>s;这时的s是2直接return stod("3")
//然后二者返回了2和3再执行加法操作返回5输出