递归一直就是老大难的问题,可每一个学编程的人都绕不开它,下面我就通过一系列案例来教大家学习一下怎么使用递归这种思想。
光说原理太没意思,我们通过一个问题来看看递归的原理,打印1到100的和,这个问题太简单了,一个循环不就搞定了嘛,比如这样
int ADD()
{
int ans=0;
for(int i=1;i<=100;i++)
ans+=i;
return ans;
}
我们看看递归怎么解决,先想一个递归公式。我们需要将规模减少,可见如果要实现这个效果我们需要引入参数。我们定义一个n,让每一次递归引用ADD2都让n-1,我们return ADD2(n-1)这样规模就变小了,不过我们要的最终结果是和,所以这个时候还需要加上当前的数字n,那么我们可以得出递归公式是return n+ADD2(n-1)。还少一点点东西,我们不能无限制的减小啊,所以给一个出口,题目要求是要1到100的和,所以我们的出口条件是n等于1时,返回 1。下面我们把程序写出来
int ADD2(int n)
{
if(n==1)
return 1;
return n+ADD2(n-1);
}
一个简单的递归就完成了。这个似乎太简单了,我们下面来一个稍微复杂一点的。
给定一个数组,使用递归求数组的任意连续位置的和
这个题目有种换汤不换药的既视感,现在我们做这个题目是不是感觉呼之欲出了,但是就是可能就是写不出来,且看下面的操作,上一个题目我们在ADD2这个函数中增加了n作为传递的参数,这个题目我们需要知道求和的数组的开始和结尾,所以我们引入更多的参数,ADD3(int start,int end)。由于需要使用主函数中的数组,所以在数组中引入ADD3(int a[],int start,int end)。现在考虑一下递归公式是什么,看下面这个图
我们把整个数组分割一下,把第一个部分分割出来,然后我们发现每一次都可以分割出来一个新的小块,那么我们尝试写一个递归公式
a[begin]+ADD3(a,begin+1,end),此时终点不用变化,起点被我们移动到下一个位置了,问题就被我们分解了。下面来看看出口是什么,因为我们是从开始start的位置一直加到end为止,由于我们让start不断增加,所以出口就很简单了。if(begin==n-1)我们这个时候应该返回什么呢,最后一个位置的start就行,return a[start]。这个函数就写好了,代码在下面了。
int ADD3(int a[],int begin,int end)
{
if(begin==end-1) return a[begin];
return a[begin]+ADD3(a,begin+1,end);
}
这个时候是不是有点感觉了。不急,来我们再看一个题目:给定两个字符串用递归的方法判断两个字符串是否相等
这次是判断两个字符串是否相同,所以我们返回bool型,由于是要比较两个字符串,所以参数就是两个字符串bool f(string s1,string s2),下面慢慢考虑如何判断字符串相同。这个题目我们一拿到手就能想到,如果两个字符串的长度不相等那么它们一定不相等
那么if(s1.length()!=s2.length) 那么return false;然后怎么做呢,还记得上面我们把数组分割出来一小块吗,这里我们来画一个图
我们把字符串对齐,从第一个位置切一刀,我们比较一下第一个位置的字符是否相同,然后我们截取红线右边的大块字符,然后每一次都对第一个位置切一刀进行判断,如图
我们发现问题基本上被我们解决了,(注:这里我用的是c/c++所以我们用substr求子串,substr(1)表示截取第二个位置到串的结尾。)我们来整理一下代码
bool f(string s1,string s2)
{
if(s1.length()!=s2.length()) return false;
if(s1[0]!=s2[0]) return false;
return f(s1.substr(1),s2.substr(1));
}
似乎我们已经完成了,不过为了严谨,我们还需要考虑一下空字符串。如果两个字符串都为空那么返回true,我们可以写成if(s1.length()==0&&s2.length()==0) return true;
bool f(string s1,string s2)
{
if(s1.length()!=s2.length()) return false;
if(s1.length()==0&&s2.length()==0) return true;
if(s1[0]!=s2[0]) return false;
return f(s1.substr(1),s2.substr(1));
}
我们这个代码还能改进一下,由于我们最先判断两个字符串个数不相同就返回false,所以我们只需要判断s1为空返回true即可
bool f(string s1,string s2)
{
if(s1.length()!=s2.length()) return false;
if(s1.length()==0) return true;
if(s1[0]!=s2[0]) return false;
return f(s1.substr(1),s2.substr(1));
}
现在我们就把这个题目完成了。
让我们回顾一下我们这三个题目解法的共通之处:先是确定返回的类型,是整型还是布尔型等等。然后我们找递归公式,这一步最为关键,我们时常需要在这一步考虑参数,如果难以递归就适当添加参数,我们找到递归公式之.最后再给递归程序寻找递归出口。
可以把常见的简单的问题改成递归问题,尝试解决,找一找递归的感觉,之后下一篇会更新递归的经典问题。