亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

JavaScript的遞歸之遞歸與循環示例介紹

2019-11-20 22:27:05
字體:
來源:轉載
供稿:網友
遞歸與循環

對于不同類型的需要重復計算的問題,循環和遞歸兩種方法各有所長,能給出更直觀簡單的方案。另一方面,循環和遞歸的方法可以互相轉換。任何一個循環的代碼都可以用遞歸改寫,實現相同的功能;反之亦然。在不失去其普遍性的前提下,可以把循環和遞歸分別用下列偽代碼概括。

偽代碼格式說明:循環采用while形式;變量不加定義;賦值用:=;條件表達式和執行的語句都寫成函數的形式,圓括號內寫上相關的值。其他語法方面,盡量接近Javascript的規范。
復制代碼 代碼如下:

//pseudo code of a loop
//while形式
function loop(arguments){
//結果的初始值
result:=initial_value;

while(condition(variable, arguments)){//循環條件,可能只需arguments,也可能為了方便引入循環變量
//計算結果。參數包括之前的結果、當前循環變量和外部變量
result:=calculate(result, variable, extern_variables);
//影響函數的外部環境,即修改外部變量
changeStatus(result, variable, extern_variables);
//執行完循環體中的語句后,修改參數或循環變量。
modify_arguments_variable(arguments, variable);
}
//返回結果
return result;
}

同樣我們給出遞歸函數的偽代碼。
復制代碼 代碼如下:

//pseudo code of a recursion
function recursion(arguments){
//以下代碼為控制函數重復調用的結構部分。
//獲得再次調用此函數的新的參數,可能為多組arguments值。
//對應于循環中的condition(variable, arguments)和modify_arguments_variable(arguments, variable)。
new_arguments:=conditional_get_next(arguments);
//對新參數的每一組,調用函數自身。
results:=recursion(new_arguments);

//以下的代碼為每次調用都運行的功能部分
//計算結果。涉及到之前的結果、當前循環變量和外部變量。
//對應于循環中的result:=calculate(result, variable, extern_variables)。
result:=calculate(arguments, extern_variables);
result:=combine(result, results);
//影響函數的外部環境,即修改外部變量
changeStatus(result, arguments, extern_variables);
return result;
}

籍比較兩段代碼,可以看出循環和遞歸具有相似的構成,通過改變順序和適當的變換,任何循環都可以用遞歸的方式實現。程序簡單時,這種轉換很容易看出。比如下面這個簡單的累計求和函數:
復制代碼 代碼如下:

//loop
function sum(num){
var result=1;
while (num>1){
result+=num;
num--;
}
return result;
}

對應的遞歸形式:

復制代碼 代碼如下:

//recursion
function sum2(num){
if (num>1){
return num+sum(num-1);
}else{
return 1;
}
}

反之,大部分遞歸程序也可以直接由循環來實現。下面是求最大公約數的循環形式的函數。
復制代碼 代碼如下:

function gcd2(a, b){
var temp;
if (a<b){
temp=a;
a=b;
b=temp;
}
var c=a%b;
while (c!==0){
a=b;
b=c;
c=a%b;
}
return b;
}

不過,從遞歸到循環的轉換并不是都這么容易。遞歸偽代碼中的產生再次調用此函數的新參數部分

new_arguments:=conditional_get_next(arguments);

較之循環的對應部分更為靈活??梢园凑招庐a生的參數組數(函數需要的所有參數為一組)將遞歸分為兩類。第一類為參數組數固定,該遞歸可以轉換為循環,比如斐波那契數列和最大公約數的例子;第二類為參數組數不確定――就像在遍歷一個圖或樹的時候那樣,每個點有任意個相鄰的點――該遞歸不能直接轉換為循環。

因為循環只能做一維的重復,而遞歸可以遍歷二維的結構。比如一棵樹中,一個節點既有它的子節點,也有同級的節點,簡單的一維循環不能夠在兩個方向上遍歷。

但是如果我們在循環中借助某種數據結構記住有關節點位置的一些信息,第二類遞歸也可以用循環實現。

我們再通過一個例子來實踐上面觀察得出的結論。HTML5為Document和Element新定義了一個方法getElementsByClassName(names),返回具有給定class值的所有elements。包括Firefox3在內的一些瀏覽器已經支持該方法。下面我們先用遞歸的方法給出一個功能較弱的版本,然后再用循環的方法重寫它。
復制代碼 代碼如下:

var getElementsByClass={};

//elem為一個HTMLElement
//name為單個class名
//返回包含elem下所有class屬性包含給定名稱的element的數組
getElementsByClass.recursion1=function (elem, name){
var list=[];
function getElements(el){
if (el.className.split(' ').indexOf(name)>-1){
list.push(el);
}
for (var i=0, c=el.children; i<c.length; i++){
getElements(c[i]);
}
}
getElements(elem);
return list;
}

如前所述,在循環中為了記住節點的位置信息,我們需要一個能實現以下方法的數據結構。

push(object) //寫入一個對象。

objectpop() //讀出最近寫入的一個對象,并將其從數據結構中刪除。

objectget() //讀出最近寫入的一個對象,不改變數據結構的內容。

堆棧正是這樣一個后進先出的數據結構。Javascript中的Array對象支持前兩種方法,我們在為其增加第三個方法即可。

采用循環的版本:
復制代碼 代碼如下:

getElementsByClass.loop1 = function(elem, name){
//use a js array as the basis of a needed stack
var stack = [];
stack.get = function(){
return stack[stack.length - 1];
}

var list = [];
//the business logic part. put the eligible element to the list.
function testElem(el){
if (el.className.split(' ').indexOf(name) > -1) {
list.push(el);
}
}
//check the root element
testElem(elem);
//initialize the stack
stack.push({
pointer: elem,
num: 0
});
var parent, num, el;
while (true) {
parent = stack.get();
el = parent.pointer.children[parent.num];
if (el) {//enter a deeper layer of the tree
testElem(el);
stack.push({
pointer: el,
num: 0
});
}
else {//return to the upper layer
if (stack.pop().pointer === elem) {
break;
}
else {
stack.get().num += 1;
}
}
}

return list;
}

歸納起來。所有循環都可以用遞歸實現;所有遞歸都可以用循環實現。采用哪種方法,由具體問題下哪種思路更方便直觀和使用者的喜好決定。

效率

性能方面,遞歸不比循環有優勢。除了多次函數調用的開銷,在某些情況下,遞歸還會帶來不必要的重復計算。以計算斐波那契數列的遞歸程序為例。求第n項A(n)時,從第n-2項起,每一項都被重復計算。項數越小,重復的次數越多。令B(i)為第i項被計算的次數,則有

B(i)=1; i=n, n-1

B(i)=B(i+1)+B(i+2); i<n-1

這樣,B(i)形成了一個有趣的逆的斐波那契數列。求A(n)時有:

B(i)=A(n+1-i)

換一個角度來看,令C(i)為求A(i)時需要的加法的次數,則有

C(i)=0; i=0, 1

C(i)=1+C(i-1)+C(i-1); i>1

令D(i)=C(i)+1,有

D(i)=1; i=0, 1

D(i)=D(i-1)+D(i-1)

所以D(i)又形成一個斐波那契數列。并可因此得出:

C(n)=A(n+1)-1

而A(n)是以幾何級數增長,這種多余的重復在n較大時會變得十分驚人。與之相對應的采用循環的程序,有

B(n)=1; n為任意值

C(n)=0; n=0, 1

C(n)=n-1; n>1

因而當n較大時,前面給出的采用循環的程序會比采用遞歸的程序快很多。

如上一節中的循環一樣,遞歸中的這個缺陷也是可以彌補的。我們只需要記住已經計算出來的項,求較高項時,就可以直接讀取以前的項。這種技術在遞歸中很普遍,被稱為“存儲”(memorization)。

下面是采用存儲技術的求斐波那契數列的遞歸算法。
復制代碼 代碼如下:

//recursion with memorization
function fibonacci4(n){
var memory = []; //used to store each calculated item
function calc(n){
var result, p, q;
if (n < 2) {
memory[n] = n;
return n;
}
else {
p = memory[n - 1] ? memory[n - 1] : calc(n - 1);
q = memory[n - 2] ? memory[n - 2] : calc(n - 2);
result = p + q;
memory[n] = result;
return result;
}
}
return calc(n);
}
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产91精品久久久| 亚洲天堂精品在线| 欧美另类在线观看| 日韩精品在线观看视频| 日韩欧美成人免费视频| 91精品国产91久久久久福利| 国产精品高潮呻吟久久av野狼| 在线成人一区二区| 久久夜色精品国产亚洲aⅴ| 色综合久久悠悠| 亚洲aⅴ日韩av电影在线观看| 91tv亚洲精品香蕉国产一区7ujn| 亚洲一级黄色片| 丝袜美腿精品国产二区| 午夜欧美不卡精品aaaaa| 亚洲黄色有码视频| 久久精品国产电影| 成人妇女淫片aaaa视频| 亚洲色图综合网| 俺也去精品视频在线观看| 国产偷国产偷亚洲清高网站| 色老头一区二区三区在线观看| 日韩在线视频中文字幕| 97在线精品国自产拍中文| 日韩精品极品视频| 亚洲视频在线观看| 久久在线观看视频| 国产视频久久网| 不卡伊人av在线播放| 亚洲国产成人91精品| 欧美成aaa人片免费看| 亚洲第一福利网| 国产精品一区二区三| 福利视频一区二区| 亚洲国产精品成人va在线观看| 91免费版网站入口| 国产精品91一区| 国产精品亚洲аv天堂网| 欧美老女人xx| 成人深夜直播免费观看| 亚洲精品aⅴ中文字幕乱码| 日韩中文娱乐网| 91色p视频在线| 国产精品日日摸夜夜添夜夜av| 国产视频精品久久久| 欧美精品在线极品| 亚洲欧美日韩另类| 国产精品久久久久久久久影视| 精品久久久久人成| 欧洲亚洲免费在线| 欧美大学生性色视频| 日本一本a高清免费不卡| 精品久久久久久国产91| 国产精品jizz在线观看麻豆| 久久国产精品首页| 色综合五月天导航| 欧美福利视频在线| 欧美日韩国产中字| 欧美日韩亚洲天堂| 欧美在线视频在线播放完整版免费观看| 久久久久久久网站| 久久国产精品久久久| 欧美成人精品激情在线观看| 日本午夜在线亚洲.国产| 亚洲欧洲中文天堂| 亚洲电影av在线| 免费不卡在线观看av| 国产精品爽爽ⅴa在线观看| 亚洲免费高清视频| 91在线高清视频| 亚洲精品一区二区三区不| 国产亚洲精品日韩| 欧美伦理91i| 国内精品久久久久久| 在线观看国产精品91| 久久久精品视频成人| 欧美专区中文字幕| 久久天天躁狠狠躁夜夜躁| 中文字幕亚洲激情| 粗暴蹂躏中文一区二区三区| 日韩专区中文字幕| 色综合久久88色综合天天看泰| 久久精品这里热有精品| 欧美日韩在线视频观看| 欧美美最猛性xxxxxx| 亚洲欧美制服综合另类| 日韩在线观看免费全| 亚洲在线观看视频网站| 成人激情视频小说免费下载| 国产精品丝袜白浆摸在线| 激情懂色av一区av二区av| 97香蕉久久夜色精品国产| 成人激情视频在线观看| 狠狠久久亚洲欧美专区| 国产成人精品久久二区二区91| 98精品国产高清在线xxxx天堂| 亚洲欧美另类人妖| 亚洲国产天堂久久综合| 欧美一级视频免费在线观看| 日韩欧美aⅴ综合网站发布| 亚洲精品美女视频| 欧美大片在线看免费观看| 57pao成人国产永久免费| 久久影视电视剧凤归四时歌| 久久成年人免费电影| 亚洲国产成人91精品| 在线视频日韩精品| 成人免费网站在线| 在线观看精品自拍私拍| 亚洲三级黄色在线观看| 亚洲精品美女久久久久| 欧美精品18videos性欧| 亚洲精品资源美女情侣酒店| 亚洲在线免费视频| 亚洲视屏在线播放| 日韩免费高清在线观看| 亚洲欧美在线免费观看| 欧美一区三区三区高中清蜜桃| 在线视频欧美日韩精品| 国产精品亚洲美女av网站| 日韩视频免费中文字幕| 久久欧美在线电影| 91精品国产亚洲| 欧美人与物videos| 亚洲精品成人久久电影| 欧美成人黑人xx视频免费观看| 国产精品aaaa| 中文字幕日韩av综合精品| 91香蕉亚洲精品| 亚洲综合成人婷婷小说| 欧美精品videosex牲欧美| 欧美日韩中文字幕日韩欧美| 国产精品高潮视频| 亚洲xxxx视频| 一区二区三区回区在观看免费视频| 亚洲福利视频专区| 91po在线观看91精品国产性色| 欧美中文字幕第一页| 国产精品久久久久9999| 亚洲欧美日韩第一区| 国产亚洲欧美日韩美女| 一本色道久久综合亚洲精品小说| 国产色婷婷国产综合在线理论片a| 久久久久成人网| 这里只有精品丝袜| 91精品综合久久久久久五月天| 久久久久久久色| 在线播放日韩专区| 午夜精品一区二区三区在线视频| 欧美日韩一区免费| 亚洲第一精品久久忘忧草社区| 亚洲成色777777在线观看影院| 国产精品久久国产精品99gif| 欧美人在线视频| 色播久久人人爽人人爽人人片视av| 亚洲国产精品人人爽夜夜爽| 欧美精品激情在线观看| 国产欧美韩国高清| 热99精品里视频精品| 亚洲成人黄色网址| 欧美一级淫片丝袜脚交| 日韩欧美精品免费在线| 韩日欧美一区二区| 欧美激情精品久久久久久免费印度|