递归:把一个问题 A 转换一个或多个子问题,我们把它们的集合叫做 B;A 的答案可以由 B 得到;对于一种确定的递归方法,从 B 的答案得到 A 的答案有且只有一种方法。
迭代:与递归的思想一样,只是在实现层面不一样。
动态规划:与递归思想一样,不同的是由 B 的答案得到 A 的答案可能有多种方法(可能),每次选择一种最优的方法去推导出 A 的答案。
分冶:与递归思想不一样,递归强调由 i 到 i+1 (或反过来)的推进,一个问题到一个紧密关联(递进关系)问题的转换;而分冶强调问题的划分,一个问题分解为多个简单的问题。
画图 1 以表示它们的不同。
图1 几种算法思想的不同
1.我们先来剖析递归和迭代的本质不同
下面给出一个斐波那契数列的两种解决方法:
//递归求解方法
int F(int i)
{
if(i < 1) return 0;
if(i == 1) return 1;
return F(i-1) + F(i - 2);
}
//迭代求解方法
int F(int i)
{
f[0] = 0;
f[1] = 1;
for(i = 2; i <= N; i++)
{
f[i] = f[i-1] + f[i-2];
}
return f[i];
}
递归和迭代的本质区别在于:
对递归而言,在本问题(称为 i 问题)一轮的求解中,它不知道 i - 1 问题是怎么求解的,它只知道只知道如何由 i - 1 问题的答案得到自己的答案。
对迭代而言,在本问题(称为 i 问题)一轮的求解中,它明确知道 i - 1 的问题是怎么求解的(有多种手段,如可以直接存储 i - 1 问题的答案,或者通过其它方法求解),并且还知道如何由 i - 1 问题的答案得到自己的答案。
我们一般要将递归求解方法转换为迭代求解方法,以获得更高的性能,这就意味着,在迭代求解中,需要处理多一个细节,即,给出 i - 1 问题的求解方法。绝大多数情况下,我们直接存储 i - 1 问题的答案来达到这一点。这也揭示了一点,迭代一般是从底向上的,如斐波那契数列求解的迭代从 0,1,2 开始求解,不断向上求解,每求解一个问题,它的 i - 1 问题的答案已经得出了,所以可以很自然的完成迭代过程。对应的递归的方向往往是确定的,只能是 i 问题到 i - 1 问题的方向,因为只存在要求 i 问题,要递归调用 i - 1 问题的处理过程,而反过来是不存在的。
有一种优化的递归结合了递归与迭代的方法,实际上可以称为自顶向下的迭代或带有记忆功能的递归:
const static int unknown = -1;
//结合迭代与递归结合的方法,也称为自顶向下的迭代,或者有记忆功能的递归
int F(int i)
{
if(f[i] != unknown) //先看 i 问题的答案有没有缓存,如果有则立即返回
{
return f[i];
}
if(i == 0) t = f[0] = 0;
if(i == 1) t = f[1] = 1;
if(i > 1) t = F(i - 1) + F(i - 2); //递归求解
return f[i] = t; //缓存结果
}
这个方法明显减少了递归调用的次数,因为一旦某个问题的解缓存了,那么就不会再递归调用它的子问题的求解过程。
2.递归与动态规划