好久没有讲算法了,今天我们就来谈谈“初学者”的第二个坑,深度优先搜索,其实也就是递归。
写在最前
相信很多人都和我一样刚开始的时候完全不知道怎么下手,甚至可以说是毫无头绪,那么我们来理一理递归到底要怎么写。
简说递归
递归就是指函数多次调用他自己本身,类似于循环结构,但是递归比循环结构更加灵活,循环能完成的题都可以改写成递归的形式,但是递归能做的题不一定能写出循环结构。递归相对于循环结构代码更加简洁,思路更加清晰,但是对于初学者而言,递归的难点在于构造,即写出递归的函数。
递归的格式——三要素
void f(){
if(...)//终止条件
return ;
f();//继续调用自己
}
一:终止条件
递归一定需要一个终止条件,即满足条件后就不再递归了,这也就和循环一样,当满足循环的终止条件就不在继续循环了,否则程序就是一个死循环
二:总是在尝试将一个问题化简到更小的规模
递归的目的就是把一个大的问题分解成若干个小的问题,直到这个小问题可以直接求解为止(结
束条件)。比如求n个数之和,如果用递归编写,我们设f函数表示求和,那么f(n)表示n个数之和。我们要知道n个数之和得先知道n-1个数之和,要知道n-1个数之和得先知道n-2个数之和,一直递归下去,最后我们需要先知道1个数之和,而一个数之和就是他本身,答案可以直接得到,就可以结束递归了。
三:父问题与子问题不能有重叠的部分
由于递归是把问题规模不停的缩小,把一个大的问题分解成若干个小问题,最后再把这些小问题的结果综合得到答案,所以分解过程中,问题之间不能重叠,当然也不能遗漏。
递归和递推的区别
递推一般是顺着推,通过已知量推导出目标量,主要用于存在递推公式的题目,使用递推就比较方
便。而递归则是逆推,通过目标量倒着推导到已知量,再逆着把已知量带入得到结果,一般适用于目标的初始状态比较复杂以及没有明确的递推公式的情况。递归的好处是屏蔽了整个计算流程,只考虑结果和边界值,剩下的都由计算机来完成。但是递归相对于递推有一个最大的问题在于过程比较冗余,效率比较低,而且递归要借助系统栈,递归次数过大容易爆栈。
上手例题
其实对于我来说,多练才能有感觉,所以来练题吧!
最大公约数和最小公倍数
题目描述
给定两个正整数 a , b a,b a,b,求他们的最大公约数(gcd)和最小公倍数(lcm)。这两个整数均在 int 范围内。
输入格式
两个整数 a a a 和 b b b,用空格分隔。
输出格式
两个整数表示答案,用空格隔开。
样例 #1
样例输入 #1
6 15
样例输出 #1
3 30
求两个数的最大公约数可以直接多次用辗转相除法,当两个数可以整除的时候,就表示除数就是最大公约数,也可以是当除数为0时,被除数就为最大公约数,这就是循环或者递归的终止条件。而最小公倍数就是用两个数的积除以最大公约数就可以了
#include<bits/stdc++.h>
#define LL long long
using namespace std;
LL gcd(LL a,LL b){
if (a%b==0)return b;
return gcd(b,a%b);
}
int main(){
LL x,y;
cin>>x>>y;
if (x>=y)
cout<<gcd(x,y)<<" "<<x*y/gcd(x,y);
else cout<<gcd(y,x)<<" "<<x*y/gcd(y,x);
return 0;
}
这个地方教你们一个投机取巧的办法:
在c++中有个直接就可以求出最大公约数的函数那就是__gcd
所以我们就可以这样写
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int main(){
LL x,y;
cin>>x>>y;
if (x>=y)
cout<<__gcd(x,y)<<" "<<x*y/__gcd(x,y);
else cout<<__gcd(y,x)<<" "<<x*y/__gcd(x,y);
return 0;
}
【模板】快速幂
题目描述
给你三个整数 a , b , p a,b,p a,b,p,求 a