刚开始接触递归方法时,感觉到这种方法很匪夷所思。很多很复杂的问题短短几行代码就能解决。当时我一直试图理清程序执行的具体过程,由于对递归认识不够每次都弄的自己很晕。在这里先不谈递归程序运行时底层的细节,先举几个小例子见识一下递归的威力。
Example1:链表逆序。
对链表逆序有很多办法,如何写一个递归的函数对链表逆序呢?
背景:以下为链表要用到的结构体
struct Node{
int num;
struct Node* next;
};
typedef struct Node* Link;
设计递归函数时,最重要的就是设计函数的签名,设计函数签名时最重要的就是设计函数所需的参数。设计函数参数时最重要的一条原则是:能够通过参数反映函数所要解决问题的规模。(表达的不太清楚)在该问题中该函数必须有一个参数:指向链表头结点的指针Link。下面是函数的签名
Link reverse (Link head);
我们输入该链表的头结点,就会对该链表逆序。并返回逆序之后的头结点。
有了该函数签名,以后的工作就好做了。根据该函数如果我们传入第二个结点的指针,那么该函数就会对除去第一个结点的链表进行逆序,然后再把第一个结点放在逆序后链表的尾部就ok了。根据以上思想写出代码如下
Link reverse(Link head)
{
static Link rhead;//逆序之后的头结点。
Link second=head->next;//取得第二个结点
head->next=NULL;//把头结点摘下来。
if(second==NULL)//如果second为空head为最后一个结点。
{
rhead=head;
return rhead;//返回逆序之后的头结点。
}
reverse(second);//对从第二个结点开始的链表逆序。逆序之后second变为最后一个结点。
second->next=head;//把head放在最后一个结点之后。
return rhead;
}
虽然大体思想有了还是有很多细节要注意的1,递归如何结束。2不要忘了把头结点摘下来。
解释以下为什么把rhead定义为static类型。函数的参数是要在栈上分配空间的,而rhead只在一次函数调用中用到,不必在每次函数调用时都为他分配空间。static类型只分配一次空间。这样能够节约空间。
Example 2 输出某个字符串的全排列。例如输入abc则输出abc acb bac bca cab cba。
下面用分治的方法解决该问题。
通过观察可以发现求abc的全排列可以分解成三个子问题:以a 开始的字符串加上bc的全排列,以b 开始的字符串加上ac的全排列,以c开始的字符串加上ab的全排列。这样原问题变成了三个相同的子问题,但是问题的规模减小了。(这是书本上的解法,我可想不到这种办法。)这要能减小问题规模,一切就好办了。
现在设计函数的签名,函数的参数必须能够反映我们解决问题的规模。用
void arrange(string & str , int begin,int end)作为函数签名。为什么参数有begin和end呢?,递归函数吗?要能解决相同性质的不同规模的问题。
根据前面的思想写出代码如下:
void arrange(string & str,int begin,int end)
{
if(begin==(end-1))
cout<<str<<endl;
for(int i=begin;i<end;i++)
{
swap(str[begin],str[i]);//c++的模板函数
arrange( str,begin+1,end);//把第一个元素固定,求剩余元素的全排列。
swap(str[begin],str[i]);
}
}
该函数实现的非常巧妙,把序列的第一个字符固定,把剩余字符的全排列放在第一个字符后面。然后更换第一个字符。那第二个swap语句是干什么的呢?把字符串回复为原来的样子。要不然就乱套了。(我说不清楚,大体意思是这样)。
Example 3这个例子就更复杂了,也更有意思。(从c语言抽象思维这本书上看的)有这要一个小游戏:桌子上放着13枚硬币,你和电脑交替的取硬币,每次只能取1、 2或3枚。谁取到最后一枚就输了。实现一个算法,使电脑采取最优的策略。
先上代码,在简单分析一下。
int findGoodMove(int nCorns)
{
for(int i=1;i<4;i++)
if(isBadPosition(nCorns-i))//对每一种选择进行测试,
return i;//取i时,对方没有好的取法。
return 0;
}
bool isBadPosition(int nCorns)
{
if(nCorns==1) return 1;//如果剩下一个,是最不利的局势。
return findGoodMove(nCorns)==0;//如果找不到取胜的取法,是最不利的局势。
}
这是一个交互递归程序,只有短短的几行代码就实现了上面的算法。表达能力有限,不解释了。提交了。