給你一個整數 n
,求恰由 n
個節點組成且節點值從 1
到 n
互不相同的 二叉搜索樹 有多少種?返回滿足題意的二叉搜索樹的種數。
示例 1:
輸入:n = 3 輸出:5
示例 2:
輸入:n = 1 輸出:1
提示:
1 <= n <= 19
【分析】
方法一:動態規劃
給定一個有序序列"1...n", 爲了構建出一棵二叉搜索樹,我們可以遍歷每個數字i,將該數字作爲樹根,將1...(i-1)作爲左子樹,將(i+1)...n作爲右子樹。接着我們可以按照同樣的方式遞歸構建左子樹和右子樹。
在上述構建的過程中,由於根的值不同,因此我們能保證每一棵二叉搜索樹爲唯一的。
由此可見,原問題可以分解成兩個規模較小的子問題,且子問題的解可以複用。因此,我們可以想到使用動態規劃來求解本題。
算法:
題目要求是計算不同二叉搜索樹的個數。爲此,我們可以定義兩個函數:
假設n個節點存在二叉排序樹的個數是G(n),令f(i)爲以i爲根的二叉搜索樹的個數,則:
G(n) = f(1) + f(2) + ... + f(n)
那麼,當i爲根節點時,其左子樹節點個數爲i - 1個,右子樹節點個數n - i個,則:
f(i) = G(i - 1) * G(n - i)
綜合以上兩個公式可以得到卡特蘭數公式(https://baike.baidu.com/item/%E5%8D%A1%E7%89%B9%E5%85%B0%E6%95%B0):
G(n) = f(1) + f(2) + ... + f(n)
= G(0)*G(n-1) + G(1)*G(n-2) + ... + G(n-1)*G(0)
class Solution: def numTrees(self, n: int) -> int: dp = [0]*(n+1) # 這裏的dp相當於卡特蘭數的變量G dp[0], dp[1] = 1, 1 for i in range(2, n+1): for j in range(1, n+1): dp[i] += dp[j-1] * dp[i-j] return dp[n]
這裏有詳細分析:
https://leetcode.cn/problems/unique-binary-search-trees/solution/bu-tong-de-er-cha-sou-suo-shu-by-leetcode-solution/
時間複雜度:O(n2),其中n表示二叉搜索樹的節點個數。dp(n)函數一共有n個值需要求解,每一次求解需要O(n)的時間複雜度,因此總時間複雜度爲O(n2)。
空間複雜度:O(n)。我們需要O(n)的空間存儲dp數組。
方法二:數學
事實上,我們在方法一中推導出的dp(n)函數的值在數學上被稱爲卡特蘭數Cn。卡特蘭數更便於計算的定義如下:
class Solution: def numTrees(self, n: int) -> int: C = 1 for i in range(n): C = C * 2*(2*i+1) / (i+2) return int(C)
時間複雜度:O(n),其中n表示二叉搜索樹的節點個數。我們只需要循環遍歷一次即可。
空間複雜度:O(1),我們只需要常數空間存放若干變量。