参考:https://blog.csdn.net/qq_61453770/article/details/122812914
一. 基本阐述
大家有时会将迭代和递归搞混,但是他们其实是有差别的.
1. 递归:在运行的过程中调用自己。
2. 迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法(或者称为一次解法),即一次性解决问题。
迭代算法是用计算机解决问题的一种基本方法,一般用于数值计算。累加、累乘都是迭代算法的基础应用。典型案例:牛顿迭代法。
在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。
在什么时候结束迭代过程?这是编写迭代程序必须考虑的问题。
不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为两种情况:
1. 一种是所需的迭代次数是个确定的值,可以计算出来;
2. 另一种是所需的迭代次数无法确定。
对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的条件。
基本步骤:
1. 确定迭代模型:分析得出前一个(或几个)值与其下一个值的迭代关系数学模型;
2. 建立迭代关系式
3. 对迭代过程进行控制
注意:迭代算法必须要有终止条件,以免陷入死循环。
二. 案例1:
1. 题目
迭代法求两个数的最大公约数(辗转相除法)
基本思路:设两数为 a、b \(a>b\),用a除以b,得a÷b=q…r1 \(0≤r1\) 。若r1=0,则(a,b)=b;若r1≠0,则再用b除以r1,得b÷r1=q…r2 (0≤r2).若r2=0,则(a,b)=r1,若r2≠0,则继续用r1除r2,如此下去,直到能整除为止。
当然a与b的大小并不需要在意,因为在第一次辗转相除后,二者的位置关系就发生了变化。
2.代码
#include <stdio.h> #include <stdlib.h> int main() { int a,b; scanf("%d %d",&a,&b); int maxgys(int a,int b); //函数原型声明(最大公因数) printf("%d",maxgys(a,b)); return 0; } int maxgys(int a,int b) { while(b!=0) { int r; r=a%b; a=b; b=r; } return a; }
三. 案例 2
1.题目
给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。例如,121 是回文,而 123 不是。
示例1:
输入:x = 121
输出:true
示例2:
输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
示例3:
输入:x = 10
输出:false
解释:从右向左读, 为 01 。因此它不是一个回文数。
这道题目虽然很简单但其中用到了迭代的思想
2.代码
bool isPalindrome(int x){ if(x<0||x%10==0&&x!=0) return false; int com=1; int begin,result; while(x/com>=10) com=com*10; while(x>0){ result=x%10; begin=x/com; if(begin!=result) return false; x=(x%com)/10; com=com/100; } return true; }
怎样区别loop、iterate、traversal和recursion这几个词
loop、iterate、traversal和recursion分别翻译为:循环、迭代、遍历和递归。
乍一看,这几个词好像都与重复( repeat )有关,但各不相同:
- 循环(loop):指的是在满足条件的情况下,重复执行同一段代码。比如,while语句。
- 迭代(iterate):指的是按照某种顺序逐个访问列表中的每一项。比如,for语句。
- 遍历(traversal):指的是按照一定的规则访问树形结构中的每个节点,而且每个节点都只访问一次。
- 递归(recursion):指的是一个函数不断调用自身的行为。比如,以编程方式输出著名的斐波纳契数列。
迭代
迭代算法是用计算机解决问题的一种基本方法。它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。
利用迭代算法解决问题,需要做好以下三个方面的工作:
确定迭代变量。在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。
建立迭代关系式。所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以使用递推或倒推的方法来完成。
对迭代过程进行控制。在什么时候结束迭代过程?这是编写迭代程序必须考虑的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的条件。
可以用迭代的算法有很经典的问题,比如兔子产子问题:假定你有一雄一雌一对刚出生的兔子,它们在长到一个月大小时开始交配,在第二月结束时,雌兔子产下另一对兔子,过了一个月后它们也开始繁殖,如此这般持续下去。每只雌兔在开始繁殖时每月都产下一对兔子,假定没有兔子死亡,在一年后总共会有多少对兔子?
还有上楼梯的走法问题:有一段楼梯有10级台阶,规定每一步只能跨一级或两级,要登上第10级台阶有几种不同的走法?
这两个问题可以参看以前写的一篇文章:趣味算法之兔子产子问题
迭代与循环
先从字面上看:
迭代:“迭”:轮流,轮番,替换,交替,更换。“代”:代替。所以迭代的意思是:变化的循环,这种变化就是轮番代替,轮流代替。
循环:不变的重复。
个人认为迭代是循环的一种,循环体代码分为固定循环体,和变化的循环体。
固定的循环举例:
for($i=0; $i < 8; $i++){ echo 'Welcome to NowaMagic'; }
上面的迭代是常见的递增式迭代。类似的还有递减式迭代,递乘式迭代。
$sum = 0; for($i = 1; $i <= 1000; $i++ ){ $sum = $sum + i; }
迭代的好处:迭代减少了冗余代码,提高了代码的利用率和动态性。
循环、迭代与递归
- 递归算法与迭代算法的设计思路区别在于:函数或算法是否具备收敛性,当且仅当一个算法存在预期的收敛效果时,采用递归算法才是可行的,否则,就不能使用递归算法。
当然,从理论上说,所有的递归函数都可以转换为迭代函数,反之亦然,然而代价通常都是比较高的。但从算法结构来说,递归声明的结构并不总能够转换为迭代结构,原因在于结构的引申本身属于递归的概念,用迭代的方法在设计初期根本无法实现,这就像动多态的东西并不总是可以用静多态的方法实现一样。这也是为什么在结构设计时,通常采用递归的方式而不是采用迭代的方式的原因,一个极典型的例子类似于链表,使用递归定义及其简单,但对于内存定义(数组方式)其定义及调用处理说明就变得很晦涩,尤其是在遇到环链、图、网格等问题时,使用迭代方式从描述到实现上都变得很不现实。 - 递归其实是方便了程序员难为了机器。它只要得到数学公式就能很方便的写出程序。优点就是易理解,容易编程。但递归是用栈机制实现的,每深入一层,都要占去一块栈数据区域,对嵌套层数深的一些算法,递归会力不从心,空间上会以内存崩溃而告终,而且递归也带来了大量的函数调用,这也有许多额外的时间开销。所以在深度大时,它的时空性就不好了。
循环其缺点就是不容易理解,编写复杂问题时困难。优点是效率高。运行时间只因循环次数增加而增加,没什么额外开销。空间上没有什么增加。 - 局部变量占用的内存是一次性的,也就是O(1)的空间复杂度,而对于递归(不考虑尾递归优化的情况),每次函数调用都要压栈,那么空间复杂度是O(n),和递归次数呈线性关系。
- 递归程序改用循环实现的话,一般都是要自己维护一个栈的,以便状态的回溯。如果某个递归程序改用循环的时候根本就不需要维护栈,那其实这个递归程序这样写只是意义明显一些,不一定要写成递归形式。但很多递归程序就是为了利用函数自身在系统栈上的auto变量记录状态,以便回溯。
原理上讲,所有递归都是可以消除的,代价就是可能自己要维护一个栈。很多情况下用递归还是必要的,它往往能把复杂问题分解成更为简单的步骤,而且很能反映问题的本质。
递归其实就是利用系统堆栈,实现函数自身调用,或者是相互调用的过程。在通往边界的过程中,都会把单步地址保存下来,知道等出边界,再按照先进后出的进行运算,这正如我们装木桶一样,每一次都只能把东西方在最上面,而取得时候,先放进取的反而最后取出。递归的数据传送也类似。但是递归不能无限的进行下去,必须在一定条件下停止自身调用,因此它的边界值应是明确的。就向我们装木桶一样,我们不能总是无限制的往里装,必须在一定的时候把东西取出来。比较简单的递归过程是阶乘函数,你可以去看一下。但是递归的运算方法,往往决定了它的效率很低,因为数据要不断的进栈出栈。