递归转非递归几个实例

原创作品,出自 “晓风残月xj” 博客,欢迎转载,转载时请务必注明出处(http://blog.csdn.net/xiaofengcanyuexj)。

由于各种原因,可能存在诸多不足,欢迎斧正!

   递归是程序设计中很重要的技巧,简单易于实现;但递归程序效率较之非递归低得多,递归函数要直接或间接的调用自身,系统栈要频繁操作,时间空间消耗很大。在要求高效的很多场合需要将递归程序改写成非递归程序,由于疏于梳理这方面的知识点,感觉对于有些递归结构有些力不从心,于是有意识的学习了一下,感觉好了很多。

      关于递归程序转非递归程序,基本通用方法是用自定义栈结构模拟递归过程,这种方法就是万金油,几乎所有递归都适用,之所以说几乎,主要考虑是暂没有见过用栈解决不了的,但碍于怕自己视野狭窄所以说几乎。如果从系统角度看递归,栈机制模拟能解决所有问题。其次,对于具体问题,可以有其他方法,直接迭代、动态规划什么的,像斐波那契数列就可以用直接迭代写成非递归的;动态规划也是直接迭代的一种,但需要转换思想,提取问题的最优子结构,像数塔问题、归并排序等都属于这类。关于这类,就不说了,这得视具体题目而定,发现问题的结构,寻找状态转移方程。

      一般的还是得用栈模拟,这里主要谈谈栈模拟。以下所有代码重在说明算法,没有考虑爆栈、溢出等涉及程序的鲁棒性因素,还请谅解。

一、汉诺塔问题

   汉诺塔是根据一个传说形成的一个问题:有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:
        每次只能移动一个圆盘;
        大盘不能叠在小盘上面。
提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则。问:如何移?最少要移动多少次?(问题描述转自维基百科)

      具体解法思想为先将A杆上面的n-1个借助C杆移到B杆,然后将A杆最低一根直接移到C,再将n-1根借助A杆从B移到C。上面隐含着递归思想将问题逐渐减小最后递推到原问题。

     此时注意栈中元素不是单一的,要保留当前状态,可以用结构体,也可以多维数组,下面给出结构体做法。

#include<iostream>
using namespace std;

const int MAXN=10000;
int m_Move=0;

/**********************************************
*汉诺塔递归解法
***********************************************/
void recurHanoi(char from,char use,char to,int n)
{
	if(0==n)
		return ;
	recurHanoi(from,to,use,n-1);
	cout<<n<<"从"<<from<<"移到"<<to<<endl;
	++m_Move;
	recurHanoi(use,from,to,n-1);
}
void recurHanoi(int n)
{
	cout<<"*********递归算法**********"<<endl;
	m_Move=0;
	recurHanoi('A','B','C',n);
	 cout<<"总共移动"<<m_Move<<"次"<<endl;
	cout<<"**************************"<<endl;
}

/**********************************************
*汉诺塔非递归解法
***********************************************/
struct Node
{
	int number;
	int id;
	char from;
	char use;
	char to;
};
void print(Node now)
{
	++m_Move;
	cout<<now.id<<"从"<<now.from<<"移到"<<now.to<<endl;
}
void notRecurHanoi(int n)
{
	cout<<"********非递归算法*********"<<endl;
	 Node myStack[MAXN];
	 Node now;
	 int top=0;
	 m_Move=0;
	 now.from='A';now.use='B';now.to='C';now.number=n;now.id=n;
	 myStack[++top]=now;
	 char from,use,to,number,id;
	 while(top>0)
	 {
		 if(1==myStack[top].number)
	 	{
			print(myStack[top]);
			--top;
		 }
		 else 
		 {
			 from=myStack[top].from;use=myStack[top].use;to=myStack[top].to;number=myStack[top].number;id=myStack[top].id;
			 --top;

			 now.from=use;now.use=from;now.to=to;now.number=number-1;now.id=id-1;
			 myStack[++top]=now;

			 now.from=from;now.use=use;now.to=to;now.number=1;now.id=id;
			 myStack[++top]=now;

			 now.from=from;now.use=to;now.to=use;now.number=number-1;now.id=id-1;
			 myStack[++top]=now;	 
		 }
	 }
	 cout<<"总共移动"<<m_Move<<"次"<<endl;
	 cout<<"**************************"<<endl;
}


int main()
{
	int n;
	while(cin>>n)
	{
		recurHanoi(n);
		notRecurHanoi(n);
	}
	return 0;
}

二、组合数

      C(n,m)=C(n-1,m)+C(n-1,m-1)      n>m

      C(n,m)=1                                    n=m或m=0

      此问题的递归形式比较简单,非递归形式得深入理解过程,直接栈模拟,注意进出栈的时机,在此不多说可以看看拙劣的代码:

#include<iostream>
using namespace std;

const int MAXN=100;

int recurCombineCount(int n,int m)
{
	if(0==m||n==m)return 1;
	else return recurCombineCount(n-1,m)+recurCombineCount(n-1,m-1);
}

int notRecurCombineCount(int n,int m)
{
	int stack[MAXN][3];
	int top=0;
	++top;
	stack[top][0]=n;stack[top][1]=m;stack[top][2]=0;
	do
	{
		if(0==stack[top][2])//计算C(n-1,m)
		{
			++top;
			stack[top][0]=stack[top-1][0]-1;stack[top][1]=stack[top-1][1];stack[top][2]=0;
			if(stack[top][0]==stack[top][1]||0==stack[top][1])
				stack[top][2]=1;
		}
		if(top>=2&&stack[top][2]>0)//计算C(n-1,m-1)
		{
			++top;
			stack[top][0]=stack[top-2][0]-1;stack[top][1]=stack[top-2][1]-1;stack[top][2]=0;
			if(stack[top][0]==stack[top][1]||0==stack[top][1])
				stack[top][2]=1;
		}
		while(top>=3&&stack[top-1][2]>0&&stack[top][2]>0)//计算C(n,m)
		//注意此处这样有错://if(top>=3&&stack[top-1][2]>0&&stack[top][2]>0)
		{
			stack[top-2][2]=stack[top-1][2]+stack[top][2];
			top-=2;
		}
	}while(top>1);
	return stack[top][2];
}

int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		cout<<"************递归算法**************"<<endl;
		cout<<"结果为:"<<recurCombineCount(n,m)<<endl;
		cout<<"*********************************"<<endl;

		cout<<"***********非递归算法*************"<<endl;
		cout<<"结果为:"<<notRecurCombineCount(n,m)<<endl;
		cout<<"*********************************"<<endl;
	}
	return 0;
}


三、McCathy函数

     M(x)=x-10                      x<100

     M(x)=M(M(x+11)            x>=100

#include<iostream>
using namespace std;


int recurMcCachy(int val)
{
	if(val>100)return val-10;
	int ret=recurMcCachy(val+11);
	return recurMcCachy(ret);
}

int notRecurMcCachy(int val)
{
	int ret=val,level=1;
	while(level>0)
	{
		if(ret>100)
		{
			--level;
			ret-=10;
		}
		else 
		{
			++level;
			ret+=11;
		}
	}
	return ret;
}

int main()
{
	int val;
	while(cin>>val)
	{
		cout<<"************递归算法**************"<<endl;
		cout<<"结果为:"<<recurMcCachy(val)<<endl;
		cout<<"*********************************"<<endl;

		cout<<"***********非递归算法*************"<<endl;
		cout<<"结果为:"<<notRecurMcCachy(val)<<endl;
		cout<<"*********************************"<<endl;
	}
	return 0;
}


         这几天持续更行中!

      由于时间有限,疏于测试,如有不足或错误,欢迎斧正!




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值