美妙的递归

         刚开始接触递归方法时,感觉到这种方法很匪夷所思。很多很复杂的问题短短几行代码就能解决。当时我一直试图理清程序执行的具体过程,由于对递归认识不够每次都弄的自己很晕。在这里先不谈递归程序运行时底层的细节,先举几个小例子见识一下递归的威力。

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;//如果找不到取胜的取法,是最不利的局势。
}

这是一个交互递归程序,只有短短的几行代码就实现了上面的算法。表达能力有限,不解释了。提交了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值