2020年3月31日 星期二

Leetcode題解 Python: Unique Binary Search Trees II

今天碰到這一題,卡關很久,來記錄一下。

二元樹算是目前遇到稍微棘手級別,棘手級別目前只有莫隊算法,還沒有花時間去弄通它。

這一題屬於「遞迴」的範圍之內,給一個數字,要你回傳有相同節點數的所有可能。

一開始直覺,就是把數列排序,取一個數字,由於是二元樹,左邊皆小於該節值,右邊則大於,所以左邊樹結就是該數字左邊的所有項目組合,右邊等同處理。

接下來,要設一個節點,該節點左邊的等於左邊的遞迴結果,右邊等同處理。


就在這裡卡了一整個下午,之前相關的遞迴幾乎是把 node 往下傳,然後回傳也是 node。如果node 傳下來,左右邊的有數種組合,但是根節點只有一個,左邊要哪一個組合?右邊要哪一個組合?還是一開始就傳許多根節?直接以根結點指定左右是不可行的。

由上而下的想法是行不通的,這題需要的是由下而上。但一開始的想法碰壁之後就一直在思考由上而下的想法,直到最後才又回歸一開始的寫法改進。

既然有各種組合,就不能回傳節點,要能涵蓋各種組合,首選是用串列。

將回傳改成串列,左右邊會各有一個串列包含數種組合,最後再左右合併,往頭傳遞。這點順利改好後,一切就順多了。

用兩個迴圈跑完所有的左右組合,這時才把該子二元樹根節實例化,就不會有共同祖先的問題,也能產出對應數量且沒有重疊的子二元樹根節。

如果左或右沒有組合,就需要換成一個 [None] 來代替,沒有組合的那邊就只會是 None 也算一種組合。

寫完整理後就如下:
class Solution:
    def generateTrees(self, n: int):
        def nextNode(ns):
            NTS = []
            for i in range(len(ns)):          
                lntree = nextNode(ns[:i])               
                rntree = nextNode(ns[i+1:])
                if not lntree: lntree = [None]
                if not rntree: rntree = [None]
                for l in lntree:
                    for r in rntree:
                        root = TreeNode(ns[i])
                        root.left, root.right = l, r
                        NTS.append(root)
            return NTS
        return nextNode(list(range(1,n+1)))
相當地直觀,但是效率並不到前中段班的。

前段班在代碼中加上 memo,將組合儲存起來,如果有就優先返回該值,沒有才遞迴並建立。

在一個區間的組合是固定的,就像1234組合起來,只會有同一種各組合可能。根如果為5、6、7...,1234就會被重複計算到,有memo就不用再算一次。

索引就以左界右界,建立二維串列儲存。
class Solution:
    def generateTrees(self, n: int):
        memo = [[None]*n for _ in range(n)]
        
        def nextNode(l, r):
            if l > r: return [None]
            elif memo[l][r]: return memo[l][r]

            NTS = []
            for i in range(l,r+1, 1):    
                print(l,  i, i+1, r,range(l,r+1, 1))
                lntree = nextNode(l, i-1)               
                rntree = nextNode(i+1, r)                 
                for lt in lntree:
                    for rt in rntree:
                        root = TreeNode(i+1)
                        root.left, root.right = lt, rt
                        NTS.append(root)
            memo[l][r] = NTS
            return NTS
        
        if n == 0: return []
        return nextNode(0, n-1)