<動態規劃>Leetcode96.不同的二叉搜尋樹

好吃!好撑!發表於2024-10-03

給你一個整數 n ,求恰由 n 個節點組成且節點值從 1 到 n 互不相同的 二叉搜尋樹 有多少種?返回滿足題意的二叉搜尋樹的種數。

解題步驟如下:

  1. 二叉搜素樹的相關概念
  2. 尋找規律
  3. 思路建立
  4. 程式碼實現

1.二叉搜尋樹的相關概念

①:若左子樹不空,則左子樹所有節點的值均小於它的根節點的值。
②:若右子樹不空,則右子樹所有節點的值均大於它的根節點的值。
③:它的左右子樹也分別為二叉搜尋樹。

2.尋找規律

當n=1時,二叉搜尋樹如圖:
image
如圖所示,n=1時,有1棵二叉搜尋樹

當n=2時,二叉搜尋樹如圖:

  • [ ]

image
如圖所示,n=2時,有2棵二叉搜尋樹

當n=3時,二叉搜尋樹如圖:

當n=3時,我們可以仔細研究一下它們的情況。

  • 以1為頭結點的二叉搜尋樹,都是左節點為0;且右節點佈局與n=2時的佈局一樣(這裡如果說數值都不一樣云云,請記住題目只求種類不是具體的每一棵樹)
  • 以3為頭結點的二叉搜尋樹,都是右節點為0;且左節點與n=2時的佈局一樣
  • 以2為頭結點的二叉搜尋樹,左右子樹各有1個節點,形態也和n=1時只有一棵樹的佈局一樣

那麼可以上述規律可以整合為:
dp[3]=以1為頭結點的搜尋樹數量+以2為頭結點的搜尋樹數量+以3為頭結點的搜尋樹數量

  • 以1為頭結點的搜尋樹數量=左子樹元素為0的搜尋樹的數量x右子樹元素為2的搜尋樹數量
  • 以2為頭結點的搜尋樹數量=左子樹元素為1的搜尋樹的數量x右子樹元素為1的搜尋樹數量
  • 以3為頭結點的搜尋樹數量=左子樹元素為2的搜尋樹的數量x右子樹元素為0的搜尋樹數量

數量為0的搜尋樹數量為dp[0]
數量為1的搜尋樹數量為dp[1]
數量為2的搜尋樹數量為dp[2]

固有:dp[3]=dp[0]dp[2]+dp[1]dp[1]+dp[2]dp[0]
接下來把思路泛化成題目需要的樣子

3.思路建立

1.定義dp陣列含義
dp[i]為1到i個不同元素組成的二叉搜尋樹節點為dp[i];dp[n]就是題意要求的搜尋樹種數。
2.推導公式
由上述推導公式可知,我們需要明確頭結點,左子樹數量,右子樹數量。故我們在這裡設j作為頭結點,那麼左子樹數量自然為(j-1)個,右子樹數量為(i-j)個。
我們可以得公式:dp[i]=dp[j-1]*dp[i-j]
(可能會有朋友疑問為什麼用相乘,打個比方說n=10的二叉搜尋樹,左子樹情況有5種,右子樹情況情況有8種,要想得出所有的情況是不是應該用相乘?)
當你用以上公式從1開始推導到n,你就會發現上述公式只是推導了某一個具體的i值,如果需要從1開始推導到n,我們需要做一個累加運算,從i=1開始運算,算完了i=1的值時,把它的結果累加到下一輪i=2的運算,這樣我們計算到n時,就把1+2+3+......+n的值都沒有差錯地累加上了。

故最後的遞推公式為dp[i]+=dp[j-1]*dp[i-j]
3.初始化
只需要考慮dp[0],若二叉搜尋樹左右子樹任意一方為空,他們結果相乘為0是不合理的,故dp[0]=1。
4.遍歷順序
由遞推公式dp[i]+=dp[j-1]dp[i-j]很容易看出,dp[i]是由比i小的狀態數推匯出來的,故是從小的數遍歷到大的數(int i =1;i<=n;i++)
在i的迴圈底下,接著進行j的迴圈。j是頭結點,從1的頭結點開始,一直遍歷到i為頭節點的情況。固有(int j=1;j<=i;j++)
(不要懵逼為什麼是i,我們定義的dp[i]裡的i就是i個不同元素組成的搜尋樹的種類!)

4.程式碼實現

點選檢視程式碼

class Solution {
public:
int numTrees(int n) {
vector dp(n+1);
dp[0]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
dp[i]+=dp[j-1]*dp[i-j];
}
}
return dp[n];
}
};

相關文章