不同的二叉搜索树
先舉例探探規(guī)律
來看看n為3的時候,有哪幾種情況。
當1為頭結點,其右子樹有兩個節(jié)點,這兩個節(jié)點的布局,和 n 為2的時候兩棵樹的布局一樣。
當3為頭結點,其左子樹有兩個節(jié)點,這兩個節(jié)點的布局,是不是和n為2的時候兩棵樹的布局也是一樣!
當2為頭結點,其左右子樹都只有一個節(jié)點,布局和n為1時只有一棵樹的布局也一樣!
發(fā)現(xiàn)到這里,其實我們就找到的重疊子問題了,也就是發(fā)現(xiàn)**可以通過dp[1] 和 dp[2] 來推導出來dp[3]**的某種方式。
dp[3],就是 元素1為頭結點搜索樹的數(shù)量 + 元素2為頭結點搜索樹的數(shù)量 + 元素3為頭結點搜索樹的數(shù)量
元素1為頭結點搜索樹的數(shù)量 = 右子樹有2個元素的搜索樹數(shù)量 * 左子樹有0個元素的搜索樹數(shù)量
元素2為頭結點搜索樹的數(shù)量 = 右子樹有1個元素的搜索樹數(shù)量 * 左子樹有1個元素的搜索樹數(shù)量
元素3為頭結點搜索樹的數(shù)量 = 右子樹有0個元素的搜索樹數(shù)量 * 左子樹有2個元素的搜索樹數(shù)量
有2個元素的搜索樹數(shù)量就是dp[2]。 有1個元素的搜索樹數(shù)量就是dp[1]。 有0個元素的搜索樹數(shù)量就是dp[0]。所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]
如圖所示:
此時我們已經(jīng)找到的遞推關系了,那么可以用動規(guī)五部曲在系統(tǒng)分析一遍。
確定dp數(shù)組(dp table)以及下標的含義
dp[i] :i個不同元素節(jié)點組成的二叉搜索樹的個數(shù) 。
確定遞推公式
以上分析中,可看出其遞推關系是: dp[i] += dp[以j為頭結點左子樹節(jié)點數(shù)量] * dp[以j為頭結點右子樹節(jié)點數(shù)量]
- j相當于是頭結點的元素,從1遍歷到i為止。
所以遞推公式:dp[i] += dp[j - 1] * dp[i - j];
j-1 為以j為頭結點左子樹節(jié)點數(shù)量,i-j 為以j為頭結點右子樹節(jié)點數(shù)量
dp數(shù)組如何初始化
推導的基礎都是dp[0],所以只初始化dp[0]就可以了。
那么dp[0]應該是多少呢?
從定義上來講,空節(jié)點也是一顆二叉樹,也是一顆二叉搜索樹,這是可以說得通的。
從遞歸公式上來講,dp[以j為頭結點左子樹節(jié)點數(shù)量] * dp[以j為頭結點右子樹節(jié)點數(shù)量] 中以j為頭結點左子樹節(jié)點數(shù)量為0,也需要dp[以j為頭結點左子樹節(jié)點數(shù)量] = 1, 否則乘法的結果就都變成0了。
所以初始化dp[0] = 1
確定遍歷順序
首先一定是遍歷節(jié)點數(shù),從遞歸公式:dp[i] += dp[j - 1] * dp[i - j]可以看出,節(jié)點數(shù)為i的狀態(tài)是依靠 i之前節(jié)點數(shù)的狀態(tài)。
用j來遍歷i里面每一個數(shù)作為頭結點時的情況。
代碼如下:
for (int i = 1; i <= n; i++) {for (int j = 1; j <= i; j++) {dp[i] += dp[j - 1] * dp[i - j];} }舉例推導dp數(shù)組
n為5時候的dp數(shù)組狀態(tài)如圖:
難點:
首先這道題想到用動規(guī)的方法來解決,就不太好想,需要舉例,畫圖,分析,才能找到遞推的關系。
然后就是確定遞推公式了,如果把遞推公式想清楚了,遍歷順序和初始化,就是自然而然的事情了。
總結