分治:字面上理解就是分而治之,就是把一个复杂的问题分成两个或多个相同或相似的子问题,再把子问题分成更小的子问题...知道最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
递归:直接或间接地调用自身的算法称为递归算法。递归的两个要素:结束条件(边界条件)和递归方程,具备这两个条件,才能在有限次计算后得出结果。
递归算法的执行效率通常较差,因此需要将递归算法转换为非递归算法。有两种方式:直接转化法和间接转换法。
1,直接转换法:
直接转换法需要使用一些变量保存中间结果。直接转换法通常用来消除尾递归和单向递归,将递归结构用循环结构来替代。
尾递归是指在递归算法中,递归调用语句只有一个,而且处在算法的最后,例如阶乘的算法:
long fact(int n)
{
if(n==0) return 1;
else return n*fact(n-1);
}
比如n=3,即return 3*fact(3-1),继续分解:return 3*2*fact(2-1),继续return 3*2*1.
当递归调用返回时,是返回到上一层递归调用的下一条语句,而这个返回位置正好是算法的结束处,所以,不必要利用栈来保存返回信息。对于尾递归形式的递归算法,可以利用循环结构来替代。所以可以写成下面的非递归算法:
long fact(int i)
{
int s=1;
for(int i=1;i<=n;i++)
s=s*i; //用s保存中间结果
return s;
}
单向递归是指递归算法中虽然有多处递归调用语句,但各个递归调用语句的参数之间没有关系,并且这些递归调用语句都处在递归算法的最后。显然,尾递归是单向递归的特例。比如求斐波那契数列的递归算法如下:
int f(int n)
{
if(n==1 || n==0) return 1;
else return f(n-1)+f(n-2); //两个递归调用语句的参数不互相依赖,他们之间没有关系。
}
对于单向递归,可以设置一些变量保存中间结果,将递归结构用循环结构来替代。比如:
int f(int n)
{
int i,s;
int s1=1,s2=1;
for(i=3;i<=n;i++)
{
s=s1+s2;
s2=s1; //保存f(n-2)的值
s1=s; //保存f(n-1)的值
}
return s;
}
2,间接转换法
该方法使用栈保存中间结果,一般需要根据递归函数在执行过程中栈的变化得到。一般过程如下:
初始状态s0进栈。
while(栈不为空)
{
退栈,将栈顶元素赋给s;
if(s是要找的结果) 返回;
else
{
寻找到s的相关状态s1;
将s1进栈
}
}
间接转化法在数据结构中有较多实例,如二叉树遍历算法的非递归实现、图的深度优化遍历算法的非递归实现等等。
因此,对求解某些问题时,我们希望用递归算法分析问题,用非递归算法具体求解问题。
递归算法转换为非递归算法有三种基本方法:
1,通过分析,跳过分解过程,直接用循环结构的算法实现求值过程。
2,自己用栈模拟系统的运行时栈,通过分析只保存必须保存的信息,从而用非递归算法替代递归算法。
3,利用栈保存参数,由于栈的后进先出特性吻合递归算法的执行过程,因而可以用非递归算法替代递归算法。