1. 程式人生 > >Js來分析遞迴

Js來分析遞迴

遞迴

相信在數學中很常見這個概念,實際在程式設計中也很常見這樣的思維。遞迴通俗的來說,就是通過不斷的將當前問題進行分解,向前追溯直到終點然後再反推求解的過程。

通俗解讀

案例一 :看電影不知道在第幾排

看電影時不清楚自己在第幾排,可以通過問前一排的人來得知,進行加1即可。那麼用遞迴的思路求解程式碼就是這樣的。

function fn = (n) {
    if(n< = 0) return '座位不存在'
    if(n>1) {
    return fn(n-1)+1
    } else {
    return 1
    }
}
複製程式碼

案例二 :走格子有多少走法

一共有n格,每步可以走1格或者2格,問一共有多少走法。 首先分解問題是第n格可以是前面n-1格走的,也可能是n-2格走的。所以fn(n) = f(n-1) + f(n-2)。也要知道終止條件是隻有1步,那麼只有一步的可能情況是還有1格,也可能是還有2格。

function fn = (n){
  if(n>2){
return fn(n-1) + fn(n-2)
} else if(n==2)   {
return 2
} else {
return 1
}
}
複製程式碼

核心要點解析

可以分解為子問題

也就是返回的遞迴子問題與當前問題的邏輯拆解關係

這個問題與分解之後的子問題,除了資料規模不同,其他都是相同的

也就是子問題的解法與當前問題是完全一致的,不需要區別寫法

有終止條件

不再進行遞迴的判斷條件,並且知道臨界條件的特殊值是可求的

實際問題

堆疊溢位

當遞迴層級過深的時候,因為在遞迴的過程中會一直把臨時變數封裝為棧壓入記憶體棧,如果一直壓入,就會導致溢位導致服務崩潰。通常的解決方案是設定一個遞迴深度來進行限制。 比如下面的程式碼:則假定記憶體深度為1000,超過1000則丟擲異常。

let depth = 0 
let f = (n) => {
++depth
if(depth>1000) throw error()
if(n===1) return 1
return fn(n-1) + 1 
}
複製程式碼

說明:這種不是很實用,因為記憶體一般是動態變化的,用定值沒意義,而如果動態獲取記憶體,又小題大做了。

重複計算

還是上面的遞迴計算走法的案例,不難發現會重複計算一些中間步驟的走法,導致浪費。當然這種問題不一定會有,和問題的分解有關。

優化方式是針對已經得到結果的走法計到Map快取中直接使用。

let  f  = ( n) => {
  if (n == 1) return 1;
  if (n == 2) return 2;
  // hasSolvedList 可以理解成一個 Map,key 是 n,value 是 f(n)
  if (hasSolvedList.containsKey(n)) {
    return hasSovledList.get(n);
  }
  ley ret = f(n-1) + f(n-2);
  hasSovledList.put(n, ret);
  return ret;
}
複製程式碼

無限遞迴

也就是沒有辦法找到終止條件的情況要考慮進,主要是避免死迴圈或者髒資料的影響

總結

本文主要介紹了常見的遞迴案例,可以用遞迴的核心點以及遞迴可能存在的問題。

彩蛋~~ 魔法幣挑戰

小易準備去魔法王國採購魔法神器,購買魔法神器需要使用魔法幣,但是小易現在一枚魔法幣都沒有,但是小易有兩臺魔法機器可以通過投入x(x可以為0)個魔法幣產生更多的魔法幣。 魔法機器1:如果投入x個魔法幣,魔法機器會將其變為2x+1個魔法幣 魔法機器2:如果投入x個魔法幣,魔法機器會將其變為2x+2個魔法幣 小易採購魔法神器總共需要n個魔法幣,所以小易只能通過兩臺魔法機器產生恰好n個魔法幣,小易需要你幫他設計一個投入方案使他最後恰好擁有n個魔法幣

如果你對這項遊戲的解法有興趣,就請跳轉下面的連結介紹吧,有程式碼有真相。