前端程式設計師學好演算法系列(十)動態規劃

2022-08-04 10:57:08 字數 4608 閱讀 8785

動態規劃整體思路是用遞迴問題求解,然後對遞迴過程中存在的大量重疊子問題進行優化, 自頂向下的求解的思路為記憶化搜尋,自底向上的解決問題的思想就是動態規劃,自頂向下的求解通常更好理解,我們理解後在改成自底向上的動態規劃求解;

劍指 offer 10- i. 斐波那契數列

寫乙個函式,輸入 n ,求斐波那契(fibonacci)數列的第 n 項。斐波那契數列的定義如下:

f(0) = 0, f(1) = 1

f(n) = f(n - 1) + f(n - 2), 其中 n > 1.

斐波那契數列由 0 和 1 開始,之後的斐波那契數就是由之前的兩數相加而得出。

答案需要取模 1e9+7(1000000007),如計算初始結果為:1000000008,請返回 1。

示例 1

:輸入:n = 2

輸出:1

示例 2

:輸入:n = 5

輸出:5

1.斐波那契的思想,就是我們求fib(n) 的解轉化為我們求 fib(n-1)+fib(n-2)  ,fib(n-1)  我們可以轉化為 fib(n-1-1) + fib(n-2-1) ,隨著遞迴進行,我們最後會得到n=1和 n=0的時候,同時我們知道 n=0時fib(0) 等於0 fib(1)等於1,

2,上述**實現了乙個斐波那契數列,但是對於斐波那契數列數列的時間複雜度是o的n次方的,因為我們求解的時候存在大量的重複求解,在上面的基礎上運用cache 快取計算結果 cache[n] 存在時直接求解,防止重複計算,

/*

* * @param n

* @return */

var fib =function(n) ;

return fibonacci(n) %1000000007n;

function fibonacci(n)

cache[n] = fibonacci(n - 1) + fibonacci(n - 2

)

return

cache[n];}};

3.我們改造成動態規劃的求解方式,自下向上的解決問題,fibonacci = 0 時 fib(0) 為0 fibonacci 為1時fib(1) =1   fibonacci[2] = fibonacci[2-1] + fibonacci[2-2] ,我們求n的解只需求解 fibonacci[n] 

/*

* * @param n

* @return */

var fib =function(n)

return

fibonacci[n];

};

我們再看乙個典型的斐波那契問題

70. 爬樓梯

假設你正在爬樓梯。需要 n 階你才能到達樓頂。

每次你可以爬 1 或 2 個台階。你有多少種不同的方法可以爬到樓頂呢?

注意:給定 n 是乙個正整數。

示例 1:

輸入: 2

輸出:

2解釋: 有兩種方法可以爬到樓頂。

1. 1 階 + 1

階2. 2 階

解題:該題和上題思路相同,我們就不深入講解了,我們使用記憶結果時可以使用物件,也可以使用陣列

1.自頂向下求解

/*

* * @param n

* @return */

var climbstairs = function (n,map=) ;

2. 自底向上求解

/*

* * @param n

* @return */

var climbstairs =function (n)

return

catche[n]

};

343. 整數拆分

給定乙個正整數 n,將其拆分為至少兩個正整數的和,並使這些整數的乘積最大化。 返回你可以獲得的最大乘積。

示例 1

:輸入:

2輸出:

1解釋:

2 = 1 + 1, 1 × 1 = 1

。示例 2:

輸入:

10輸出:

36解釋:

10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

解題:

1.我們求分割n獲得的最大乘積,需要求把n分成 1和n-1的成績 2和n-2 的成績 到n-1和1的最大成績,在這個結果中取最大值,

2.每次n可以選擇當前值,或者i*(n-i) 不在分割n,和i* 繼續分割n-i的結果i*d(n-i)

3.我們定義dp物件快取n的最大值的結果,防止重複求解

/*

* * @param n

* @return */

var integerbreak =function(n)

function d(n)

if(dp[n]!==undefined)

let res = -1

for(let i =1;i)

dp[n] =res

return

dp[n]

}return

d(n)

};

解題二狀態陣列dp[i]表示:數字 i 拆分為至少兩個正整數之和的最大乘積。為了方便計算,dp 的長度是 n + 1,值初始化為 1。

顯然dp[2]等於 1,外層迴圈從 3 開始遍歷,一直到 n 停止。內層迴圈 j 從 1 開始遍歷,一直到 i 之前停止,它代表著數字 i 可以拆分成 j + (i - j)。但 j * (i - j)不一定是最大乘積,因為i-j不一定大於dp[i - j](數字i-j拆分成整數之和的最大乘積),這裡要選擇最大的值作為 dp[i] 的結果。

空間複雜度是 o(n)o(n),時間複雜度是 o(n^2)o(n的2次方)

/*

* * @param n

* @return */

var integerbreak =function(n)

}return

dp[n];

};

198. 打家劫舍

你是乙個專業的小偷,計畫偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。

給定乙個代表每個房屋存放金額的非負整數陣列,計算你 不觸動警報裝置的情況下 ,一夜之內能夠偷竊到的最高金額。

示例 1:

輸入:[1,2,3,1

]輸出:

4解釋:偷竊

1 號房屋 (金額 = 1) ,然後偷竊 3 號房屋 (金額 = 3

)。偷竊到的最高金額 = 1 + 3 = 4 。

解題:1. 對於乙個房間我們可以選擇偷取也可以選擇不偷取,如果偷取的話我們下次選擇需要選擇n+2的房間來嘗試偷取,結果取最大值

2. 我們求解的值是不考慮偷取當前房間和考慮偷取當前房間的最大值 ,res = math.max(res,nums[i]+room(nums,i+2)) , 因為i+2 可能越界,因此當nums.length<=index時我們直接return 0

3.我們用tests 儲存是否已經偷過該房間,如果tests訪問過直接返回值,如果沒有訪問過,我們把求解的n的值儲存在tests中

/*

* * @param nums

* @return */

var rob =function(nums)

if(tests[index]!==-1

) let res = 0

for(let i =index;i)

tests[index] =res

return

tests[index]

}return room(nums,0

)

};

解法2動態規劃方程:dp[n] = max( dp[n-1], dp[n-2] + num )

由於不可以在相鄰的房屋闖入,所以在當前位置 n 房屋可盜竊的最大值,要麼就是 n-1 房屋可盜竊的最大值,要麼就是 n-2 房屋可盜竊的最大值加上當前房屋的值,二者之間取最大值

舉例來說:1 號房間可盜竊最大值為 33 即為 dp[1]=3,2 號房間可盜竊最大值為 44 即為 dp[2]=4,3 號房間自身的值為 22 即為 num=2,那麼 dp[3] = max( dp[2], dp[1] + num ) = max(4, 3+2) = 5,3 號房間可盜竊最大值為 55

時間複雜度:o(n)o(n),nn 為陣列長度

/*

* * @param nums

* @return */

var rob =function(nums)

return

dp[len];

};

前端程式設計師學好演算法系列(八)二叉樹和遞迴

257.二叉樹的所有路徑 給定乙個二叉樹,返回所有從根節點到葉子節點的路徑。說明 葉子節點是指沒有子節點的節點。示例 輸入 1 23 5 輸出 1 2 5 1 3 解釋 所有根節點到葉子節點的路徑為 1 2 5,1 3 解題 1.root null 直接return 2.判斷我們的路徑是否到達到乙個...

程式設計師的自我修養(十)動態鏈結

動態鏈結其實就是把鏈結的過程推遲到了執行時再進行。特點 重定位位址無關 fpic與 fpie 模組內部的函式呼叫 跳轉等 這種不需要重定位 模組內部的資料訪問,比如模組中定義的全域性變數 靜態變數 相對定址 模組外部的函式呼叫 跳轉等 got,但是儲存的是目標函式的位址 模組外部的資料訪問,比如其他...

程式設計師筆試面試演算法題系列 陣列

1 遞迴實現陣列求和 void sum int a,int n,double sum 每次遞迴,在sum上累加,同時n自減,當n 0,退出遞迴。2 利用乙個for迴圈列印二維與三維陣列 double total row col for int k 0 k 3 遞迴判斷陣列是否遞增 return a ...