递归:程序调用自身的编程技巧称为递归(recursion)
迭代:对一组指令或一定步骤进行重复执行称为迭代(iterator)
先来看一个例子
//求连续正整数的和值
//迭代
int GetSum_1(int m) {
int i = 1, sum = 0;
while (i <= m) {
sum = sum + i;
i++;
}
return sum;
}
//递归
int GetSum_2(int n) {
if (n <= 0) {
return 0;
} else {
return n + GetSum_2(n - 1);
}
}
我们可以看到,迭代就是每一次对过程的重复,而每一次迭代得到的结果会作为下一次迭代的初始值。那么做好迭代我们至少需要准备以下三个方面
1.确定变量
在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。
2.建立关系式
所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以使用递推或倒推的方法来完成。
3.过程控制
在什么时候结束迭代过程?这是编写迭代程序必须考虑的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的条件。
再来看看递归,程序中有直接或间接调用自身的一种方法。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
那么构成递归需具备的条件有两个:
1. 子问题须与原始问题为同样的事,且更为简单。
2. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。
对于递归和迭代的区别我们可以再来看一个经典的例子——斐波那契数
// 斐波那契数它的前两项均为1,从第三项开始各项均为前两项之和 (1 1 2 3 5 8 13...)
//迭代
int Fibonacci_2(int num) {
int f0 = 1, f1 = 1, currentNum = 0;
if (num == 1 || num == 2) {
return 1;
} else {
for (int i = 2; i < num; i++) {
currentNum = f0 + f1;
f0 = f1;
f1 = currentNum;
}
return currentNum;
}
}
//递归
int Fibonacci_1(int num) {
if (num == 1 || num == 2) {
return 1;
} else {
return Fibonacci_1(num - 1) + Fibonacci_1(num - 2);
}
}
从这个例子我们可以看出,迭代算法明显没有递归算法简洁,但是迭代算法效率高,运行时间正比于循环次数,而且没有调用函数引起的额外开销。递归版本的代码很简介清晰,可读性强。但是递归存在一个致命的缺点就是,递归的深度太深会导致堆栈溢出!我们注意到,每一次调用自身函数的时候,该函数都没有退出,而是继续运行。在函数调用过程中,系统会分配一个堆栈,当递归深度越深,堆栈的占用就越大,造成的后果就是会产生堆栈溢出。
小结
从写法上
迭代:循环结构,例如for,while循环
递归:选择结构,例如if else 调用自己,并在合适时机退出
从代码量上
迭代:多
递归:少
从性能上
迭代:快
递归:慢
推荐:能不用递归就不用递归,递归一般都可以用迭代来代替。