- 算法爲王。
- 排序算法博大精深,前輩們用了數年甚至一生的心血研究出來的算法,更值得咱們學習與推敲。
由於以後要講有內容和算法,其代碼的實現都要用到遞歸,因此,搞懂遞歸很是重要。前端
現實例子:週末你帶着女友去電影院看電影,女友問你,我們如今坐在第幾排啊 ?電影院裏面太黑了,看不清,無法數,如今你怎麼辦 ?git
因而你就問前面一排的人他是第幾排,你想只要在他的數字上加一,就知道本身在哪一排了。 可是,前面的人也看不清啊,因此他也問他前面的人。 就這樣一排一排往前問,直到問到第一排的人,說我在第一排,而後再這樣一排一排再把數字傳回來。 直到你前面的人告訴你他在哪一排,因而你就知道答案了。github
基本上,全部的遞歸問題均可以用遞推公式來表示,好比:算法
f(n) = f(n-1) + 1;
// 其中,f(1) = 1
複製代碼
f(n) 表示你想知道本身在哪一排,f(n-1) 表示前面一排所在的排數,f(1) = 1 表示第一排的人知道本身在第一排。json
有了這個遞推公式,咱們就能夠很輕鬆地將它改成遞歸代碼,以下:數組
function f(n) {
if (n == 1) return 1;
return f(n-1) + 1;
}
複製代碼
一個問題只要同時知足如下 3 個條件,就能夠用遞歸來解決。瀏覽器
何爲子問題 ?就是數據規模更小的問題。 好比,前面講的電影院的例子,你要知道,本身在哪一排
的問題,能夠分解爲前一排的人在哪一排
這樣一個子問題。bash
好比電影院那個例子,你求解本身在哪一排
的思路,和前面一排人求解本身在哪一排
的思路,是如出一轍的。數據結構
好比電影院的例子,第一排的人不須要再繼續詢問任何人,就知道本身在哪一排,也就是 f(1) = 1,這就是遞歸的終止條件。函數
1. 遞歸代碼編寫
寫遞歸代碼的關鍵就是找到如何將大問題分解爲小問題的規律,而且基於此寫出遞推公式,而後再推敲終止條件,最後將遞推公式和終止條件翻譯成代碼。
2. 遞歸代碼理解
對於遞歸代碼,若試圖想清楚整個遞和歸的過程,其實是進入了一個思惟誤區。
那該如何理解遞歸代碼呢 ?
所以,理解遞歸代碼,就把它抽象成一個遞推公式,不用想一層層的調用關係,不要試圖用人腦去分解遞歸的每一個步驟。
function fact(num) {
if (num <= 1) {
return 1;
} else {
return num * fact(num - 1);
}
}
fact(3) // 結果爲 6
複製代碼
如下代碼可致使出錯:
var anotherFact = fact;
fact = null;
alert(antherFact(4)); //出錯
複製代碼
因爲 fact 已經不是函數了,因此出錯。
使用 arguments.callee
arguments.callee 是一個指向正在執行的函數的指針,arguments.callee 返回正在被執行的對現象。 新的函數爲:
function fact(num){
if (num <= 1){
return 1;
}else{
return num * arguments.callee(num - 1); //此處更改了。
}
}
var anotherFact = fact;
fact = null;
alert(antherFact(4)); // 結果爲 24
複製代碼
先看圖
葉子結點:就是深度爲 0 的結點,也就是沒有孩子結點的結點,簡單的說就是一個二叉樹任意一個分支上的終端節點。
數據結構格式,參考以下代碼:
const json = {
name: 'A',
children: [
{
name: 'B',
children: [
{
name: 'E',
},
{
name: 'F',
},
{
name: 'G',
}
]
},
{
name: 'C',
children: [
{
name: 'H'
}
]
},
{
name: 'D',
children: [
{
name: 'I',
},
{
name: 'J',
}
]
}
]
}
複製代碼
咱們如何獲取根節點的全部葉子節點個數呢 ?
遞歸代碼以下:
/**
* 獲取根節點的全部 葉子節點 個數
* @param {Object} json Object 對象
*/
function getLeafCountTree(json) {
if(!json.children){
return 1;
} else {
let leafCount = 0;
for(let i = 0 ; i < json.children.length ; i++){
// leafCount = leafCount + getLeafCountTree(json.children[i]);
leafCount = leafCount + arguments.callee(json.children[i]);
}
return leafCount;
}
}
複製代碼
遞歸遍歷是比較經常使用的方法,好比:省市區遍歷成樹、多叉樹、階乘等。
JavaScript 數據結構與算法之美 的系列文章。
若是有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。
參考文章: