Javascript递归递归和循环的一个例子
递归和循环针对不同类型的重复计算的需要,递归循环,每一个人都有自己的好点的两种方法,可以更为直观和简单的方案。另一方面,循环和递归的方法是可以互相转换的。任何循环码可以递归重写来实现同样的功能;反之亦然。不失一般性,循环和递归可以总结为下面的伪代码,分别。
伪代码格式规范:循环是以时间的形式出现的;变量没有定义;赋值是:=;条件表达式和执行语句被写成函数,括号中的值被写入。其他语法方面尽可能接近Javascript规范。
复制代码代码如下所示:
循环的伪代码
窗体
函数循环(参数){
初始值的结果
结果:= initial_value;
而(条件(变量,参数)){条件,可能只需要参数,也可以引入,以方便循环变量。
参数包括以前的结果、当前循环变量和外部变量。
结果:=计算(结果变量,extern_variables);
影响外部环境的功能,即修改外部变量。
changestatus(结果变量,extern_variables);
在循环语句的主体中执行,或修改循环变量的参数。
modify_arguments_variable(参数变量);
}
返回结果
返回结果;
}
同样,我们给出了递归函数的伪码。
复制代码代码如下所示:
递归的伪代码
函数递归(参数){
以下代码为结构控制功能重复部分。
再次获取新参数,调用这个函数,可以是多组参数值。
对条件的周期对应 / /(变量参数)和modify_arguments_variable(参数变量)。
new_arguments:= conditional_get_next(参数);
对每个组的新参数,调用函数本身。
结果:=递归(new_arguments);
以下代码为每个调用操作功能部分
结果包括前面的结果、当前循环变量和外部变量。
结果周期对应 / /:=计算(结果变量,extern_variables)。
结果:=(参数计算,extern_variables);
结果:=合并(结果,结果);
影响外部环境的功能,即修改外部变量。
changestatus(结果,争论,extern_variables);
返回结果;
}
比较两段代码,我们可以看到循环和递归有相似的结构。任何循环都可以通过改变顺序和适当的变换来递归实现:
复制代码代码如下所示:
循环
函数和(努姆){
var结果= 1;
当(数字> 1){
结果;
Num --;
}
返回结果;
}
相应的递归形式:
复制代码代码如下所示:
递归
功能2(NUM){
如果(数字> 1){
返回num +和(num-1);
其他{ }
返回1;
}
}
另一方面,大多数递归程序都可以直接从循环中实现。
复制代码代码如下所示:
功能gcd2(A,B){
VaR的温度;
如果(< b){
温度=;
甲=乙;
B =临时温度;
}
var C =一% B;
而(c)!= 0){
甲=乙;
乙=丙;
c =一% B;
}
返回B;
}
然而,从递归到循环的转换并不那么容易,函数的新参数部分在递归伪代码中再次生成。
new_arguments:= conditional_get_next(参数);
它比相应的周期部分更灵活。递归可分为两类根据新生成的参数组数(所有参数的函数需要一组),第一个参数组数是固定的,递归可以转化为循环,如斐波那契数列和GCD的例子;其次是一些未知参数组时,遍历图或树的每个点有相邻的点--递归不能直接转换为循环。
因为它只能在一个尺寸重复,递归可以被二维结构。例如,在一棵树上,一个节点有其子节点和同级节点,和一个简单的一维环不能走过两个方向。
但是如果我们记住一些数据中的节点位置的信息,那么第二种递归也可以在循环中实现。
我们可以用一个例子来实践观察above.html5定义了一种新的方法的结论,getelementsbyclassname(地名)的文档和元素,返回与给定类的所有元素值。一些浏览器,包括Firefox3,支持的方法。在这里,我们先给出一个功能少版采用了递归的方法,然后将它一个循环。
复制代码代码如下所示:
无功getelementsbyclass = { };
/ /元素是HtmlElement
名称是一个类名。
返回一个数组包含所有元素/类属性包含元素的名字
GetElementsByClass。recursion1 =功能(元素、名称){
var列表{ };
功能getelements(EL){
如果(el.classname.split('),IndexOf(人名)> 1){
List.push(EL);
}
对于(var i = 0,C = el.children;i < c.length;i++){
getelements(C {我});
}
}
getelements(元);
返回列表;
}
如前所述,为了记住循环中节点的位置信息,我们需要一个能够实现以下方法的数据结构。
推送(对象)写入对象。
(objectpop) / /读最近写的一个对象,并将它们从数据结构。
(objectget) / /读最近写的一个对象,不改变数据结构的内容。
堆栈是这样一个前进的数据结构。Javascript中的数组对象支持前两种方法,我们为它添加了第三种方法。
使用循环版本:
复制代码代码如下所示:
getelementsbyclass.loop1 =功能(元素、名称){
使用一个JS数组作为所需堆栈的基础
var堆栈{ };
stack.get =函数(){
{ 1 } stack.length返回栈;
}
var列表{ };
业务逻辑部分。将符合条件的元素放在列表中。
功能testelem(EL){
如果(el.classname.split('')。IndexOf(名)> 1){
List.push(EL);
}
}
检查根元素
testelem(元);
初始化堆栈
Stack.push({
指针:元素,
Num:0
});
var父,El;
当(真){
母= stack.get();
EL =父。指针;
如果(EL)输入到树的更深一层
testelem(EL);
Stack.push({
指针:EL,
Num:0
});
}
否则,返回到上层。
如果(堆栈。流行)(。指针=元){
打破;
}
{其他
stack.get(Num)= 1;
}
}
}
返回列表;
}
综上所述,所有的循环都可以递归地实现,所有的递归都可以在一个循环中实现,使用哪种方法,哪种方式更方便,更直观,用户的偏好取决于具体问题。
效率
在性能上,递归在回路没有优势。除了多个函数调用的开销,在某些情况下,递归也带来了不必要的重复计算。计算斐波那契数列的递归过程为例,项目N个(N),每个项目都是从较小的、更多的重复项n-2.the号码重复计算。次数B(I)计算的项目我是
B(i)= 1;i = n,n-1
B(i)=b(i + 1)+ b(i + 2);i < n-1
因此,B(I)形成了一个有趣的逆斐波那契数列:
B(我)=一个(n +我)
从另一个角度来看,C(i)所需的a(i)的添加数量是可用的。
c(i)=0;i=0, 1
C(我)= 1 + C(i-1)+ C(i-1);我> 1
d(i)=c(i)+ 1,与
d(i)=1;i=0, 1
D(我)= D(i-1)+ D(i-1)
所以D(I)和斐波那契数列的形成:
c(n)=a(n + 1)- 1
和(n)是以几何级数增长的,当n比较大时,这种多余的重复变得非常令人惊讶,
B(n)=1;n为任意值
c(n)=0;n=0, 1
c(n)=n-1;n>1
因此,当n较大时,使用前面给出的循环的程序将比递归程序快得多。
就像前一节中的循环一样,递归中的这个缺陷也可以被弥补。我们只需要记住已经计算过的项目,当我们要求更高的项目时,我们可以直接阅读以前的项目。这种技术在递归中很常见,也就是记忆。
以下是用于存储技术的斐波那契递归算法。
复制代码代码如下所示:
带记忆的递归
功能fibonacci4(N){
var内存用于存储每一个};计算项目
函数钙(n){
var结果,p,q;
如果(n<2){
内存{ n=n;
返回N;
}
{其他
内存= { 1 - }内存1(1);
q =内存{ 2–}内存2(2);
结果= q + q;
内存结果;
返回结果;
}
}
返回计算器(n);
}