本来以为可以不用写这一篇文章的,奈何最近学弟学妹反映深度优先搜索听不懂,原因可能与递归有关?那就写一篇文章,以我微薄的水平尝试阐述一下递归的伟大思想.
首先看定义: 程序调用自身的编程技巧称为递归
然后让我们用一个图,抽象出程序执行过程可能出现的情况:
其中上图的箭头表示程序语句执行的方向,我们知道正常程序,都是从上到下线性执行的,如上图,而调用函数的程序,它会跳转到另一个函数中去执行,结束后会回到断点处继续执行原程序,总体上还是线性的.那么递归呢? --递归也只是调用函数,只不过是在一个函数内部调用该函数自身,进入一个新的"自我执行"的阶段,而且在这个阶段里面又会遇见一条调用自身的语句,从而开始新一轮的"轮回".但如果设计得当,我们让函数在适当的时候停止调用自身,而是返回,那么这些函数也一定会在一个点不满足递归的条件,从而层层返回,直到最后一个递归函数被返回,进入原程序继续执行到结束的过程
所以我们可以归纳一下递归的几个要素/性质:
1.函数内部由自我调用语句;
2.必须存在递归出口;
如此,我们可以写一个基础的递归通式:
void fun()
{
if(递归出口 == true)
return;
fun();
}
上面的代码虽然简单,却反映了递归的两个性质,并非所有的递归函数都是这样写,但是所有的递归函数必定满足上面的性质.
我们来看几个简单的例子:
1.递归求斐波那契数列
int fib(int x)
{
if(x == 1 || x == 2) return 1;
return fib(x-1) + fib(x-2);
}
2.递归求阶乘
int factorial(int x)
{
if(x == 0 || x == 1) return 1;
return x*factorial(x-1);
}
以上都是我们熟悉的递归,因为斐波那契数列和阶乘在数学上都是递归定义的,所以递归实现非常方便.那为什么递归出口如此小的一个返回值,到最后能计算出非常大的数的阶乘或者斐波那契数列的第很多位呢?原因在于递归的第二个返回值,即if落空后执行的语句,这条语句将反复执行很多次才能执行到if条件满足,此时层层返回,虽然一开始的返回值很小,但是返回的路径很深,导致结果就像滚雪球一样慢慢积累,到最后一个返回结束后,结果就很大了.
下面我们再仿照上面的形式,写一些我们不太熟悉的递归函数:
1.递归判断回文串
bool parlindrome(string str,int l,int r)
{
if(l == r || r-l == 1) return 1;
if(str[l] != str[r]) return 0;
return parlindrome(str,l+1,r-1);
}
2.输出一些数字的全排列
bool vis[10];
void permutation(int a[],int cnt,int len)
{ //函数第一次执行前,将vis数组全部标记为false;
if(cnt == len)
{
for(int i = 0; i < len; i++)
cout<<a[i];
cout<<endl;
return;
}
for(int i = 1; i <= len; i++)
{
if(!vis[i])
{
vis[i] = 1;
a[cnt] = i;
permutation(a,cnt+1,len);
vis[i] = 0;
}
}
}
以上两个例子的递归就不是这么寻常的了,当然,也很简单.
其中1是根据回文串的对称性质,不断缩小范围,直到某个时候不匹配或者达到终点,则返回false或true,否则一直递归下去
2则是使用了深度优先搜索的思想,将一串数字1~len的字典序全排列输出出来,关键在于理解vis[i] = 0的操作,这一步是整个递归的精华所在,其详细原理相信读者耐心观摩细细体会一定能够悟透,如果实在不会,请移步我的另一篇文章--神奇的搜索--深度优先和广度优先
下面我们总结一下什么时候可能用到递归:
1.一个问题能够拆分成规模更小的子问题,且子问题的解决方式和该问题完全一致,比如快排的递归(处理左右区间规模更小,但是排序思想和处理整个序列一样),回文串判断的递归(处理n-2的序列规模比n小,但是判断回文的方式一致)
2.深度优先搜索的时候,虽然直接套第1条你会发现可能上面的全排序递归好像不存在子问题?(都是一个一个数字来的),但正是由于深度优先的思想,使得它可以使用递归实现--为什么?因为深度优先是一种"不撞南墙不回头"的策略,撞了南墙回头怎么办?我们需要回溯,而回溯,恰恰是递归所擅长的地方,每个递归的出口都是一个回溯条件!
3.很多优秀的算法的逻辑层面的思想就是自我调用,因此可以用递归来实现,比如匈牙利算法求二分图匹配时的Find函数
4.大概就以上这些吧,其实递归还是很好理解的,实在觉得难可以画画流程图,帮助理解