递归和循环

递归算法转换为非递归算法


递归算法实际上是一种分而治之的方法,它把复杂问题分解为简单问题来求解.

对于复杂问题,递归算法是一种自然且合乎逻辑的解决问题的方式,但是递归算法的执行效率通常比较差.

因此,在求解某些问题时,常采用递归算法来分析问题,用非递归算法来求解.


将递归算法转化为非递归算法有两种方法: 

方法一, 直接转化法 (直接求值,不需要回溯).   

方法二, 间接转化法(不能直接求值,需要回溯).


1. 直接转化法: 通常用来消除尾递归和单向递归,将递归结构用循环结构代替


(1)尾递归是指在递归算法中,递归调用语句只有一个,而且是处在算法的最后

例如:

阶乘的递归算法:

long fact (int n)
{
     if(n == 0)
           return 1;
     else
           return  n*fact(n-1);
}

分析: 当递归调用返回时,是返回到上一层递归调用的下一条语句,而这个返回位置正好是算法的结束处,

所以不需要栈来保存返回信息.

上述尾递归例题可以用循环结构来代替,如下:

long fact(int n)
{
        int s = 1;
        for(int i=1;i<=n;i++)
                 s = s + i;
        return s;
}
 
 

(2) 单向递归是指递归算法中虽然有多处递归调用语句,但各递归调用语句的参数之间没有关系,

并且这些递归语句都处在递归算法的最后.显然尾递归是单项递归的特例.

例如:

求斐波那契数列的递归算法:

int f(int n)
{
	if(n==0)
		return 0;
	if(n==1)
		return 1;
	return f(n-1)+f(n-1);
}


对于单向递归,可以设置一些变量保存中间结果,将递归结构用循环结构代替.

上述单向递归的斐波那契数列用循环实现,如下:

int f(int n)
{
	int i,s;
	int s1=1,s2 = 0;
        for(i=1;i<n;i++)
       {
             s = s1+s2;
             s2= s1;       //用来保存f(n-2)的值
	     s1=s;          //用来保存f(n-1)的值
        }
        return f(n-1)+f(n-1);
}
 
 

2. 简介转化法:

该方法使用栈保存中间结果,一般需要根据递归函数在执行过程中栈的变换得到.

其一般过程如下:

将初始状态s0入栈;
while(栈不为空)
{
     将栈顶元素赋值给s,出栈;
     if(s是要找的结果)
            返回;
      else{
            继续寻找;
            将s1入栈;
      }
}


这是数据结构C++版教材中的介绍的.

我感觉还是具体情况具体分析,没有固定的套路,只要把问题分析清楚了,代码也就不难写了


总结一下:

其递归过程是: 

①先将n个问题规模按照一定步长分解为n-1个问题规模,再分解为n-2个问题规模,依次执行这个过程,最后将问题规模分解为1.

即先让问题规模n入栈,再让n-1个问题规模入栈,依次入栈,最后栈顶为问题规模1.

②从问题规模1开始计算并返回,再计算问题规模2并返回,依次执行,直到问题规模n计算完毕并返回

此过程即为出栈过程.


因此递归的过程就是将大问题分解为小问题,从小问题着手各个击破,因此也验证了分而治之的思想




3. 递归规过程有无返回值


上述的阶乘和斐波那契数列都是有返回值的函数


下面来讨论一下无返回的情况

阶乘无返回值:

void fact (int n , int &sum) //sum初始化为1
{
     if (n == 0)
	{
		return;
	}
	else
	{
	        //sum = sum*n;     //sum放在递归之前,即在入栈的过程中将问题解决
		fact(n - 1, sum);
		sum = sum * n;   //sum放在递归之后,即在出栈的过程中将问题解决
	}
}


斐波那契数列无返回值:

//无返回值,斐波那契数列求值
void Fibonaci(unsigned int n, unsigned int &sum)
{
	if (n == 0)
	{
		return ;
	}
	if (n == 1)
	{
		sum = sum+1;
		return;
	}
	
	unsigned int a = 0;
	unsigned int b = 0;
	Fibonaci(n - 1, a);
	Fibonaci(n - 2, b);
	sum = a + b;
}


总结: 上述的递归返回值使用函数参数来代替.

由此可以看出使用递归求值时,有返回值的递归函数比无返回值的函数要更简洁且更易理解.

当使用递归的过程解决问题时,就没必要使用返回值.

递归函数有无返回值哪个更合适,这需要具体情况具体分析了.


这也是设计一般函数选择有无返回值的判断依据:

①如果需要计算完成时的结果只有一个,则可以用返回值直接返回.

②如果返回结果是多个,则需要函数参数辅助返回(即,使用参数的指针或引用传递).



猜你喜欢

转载自blog.csdn.net/mayi_xiaochaun/article/details/49208717