BST Question Summary

Note:
During practice, also practise how to think, how to take proper note to assist thinking, and how to draw the right helper diagram. For example, how to draw:

  • queue
  • stack

How to draw each iteration of while/for loop. How to draw recursion / divide-and-conquer's thinking process.


Questions with easy difficulty:

110. Check Balanced Binary Tree

  • This problem is generally believed to have two solutions: the top down approach and the bottom up way.

1.The first method checks whether the tree is balanced strictly according to the definition of balanced binary tree: the difference between the heights of the two sub trees are not bigger than 1, and both the left sub tree and right sub tree are also balanced. With the helper function depth(), we could easily write the code;

class solution {
  public: 
  int depth (TreeNode *root) { 
    if (root == NULL) return 0; 
    return max (depth(root -> left), depth (root -> right)) + 1; 
  } 
  bool isBalanced (TreeNode *root) { 
    if (root == NULL) return true; 
    int left=depth(root->left); 
    int right=depth(root->right); 
    return abs(left - right) <= 1 && isBalanced(root->left) && isBalanced(root->right); 
  }
};
# use bottom up approach: 
class Solution(object):
    def isBalanced(self, root):
        return self._isBalanced(root) != -1
        
    # think: can we do it without helper function? 
    # how should we modify the return result in that case? 
    def _isBalanced(self, root):
        if not root:
            return 0
        left_height = self._isBalanced(root.left)
        if left_height == -1:
            return -1
        right_height = self._isBalanced(root.right)
        if right_height == -1:
            return -1
        if abs(left_height - right_height) > 1:
            return -1
        return max(left_height, right_height) + 1
        

102. Binary Tree Level Order Traversal

Three compact python solutions

def levelOrder(self, root): 
    ans, level = [], [root] 
    while root and level: 
        ans.append([node.val for node in level]) 
        LRpair = [(node.left, node.right) for node in level] 
        level = [leaf for LR in LRpair for leaf in LR if leaf] 
    return ans

This is equivalent to:

def levelOrder(self, root): 
    if not root: 
        return [] 
    ans, level = [], [root] 
    while level: 
        ans.append([node.val for node in level]) 
        temp = [] 
        # this additional step is to make sure we only add valid (not None) leaf to level. 
        # In graph BST, we assume there won't be such None connection, therefore no need this examination
        for node in level: 
            temp.extend([node.left, node.right]) 
        level = [leaf for leaf in temp if leaf] 
    return ans

This cpp solution is more elegant.

vector<vector<int>> ret;
void buildVector(TreeNode *root, int depth) { 
    if(root == NULL) return; 
    if(ret.size() == depth) 
      ret.push_back(vector<int>());
    ret[depth].push_back(root->val); 
    buildVector(root->left, depth + 1); 
    buildVector(root->right, depth + 1);
  }
  vector<vector<int> > levelOrder(TreeNode *root) { 
    buildVector(root, 0); 
    return ret;
  }

There is other solution which use a queue. Also check them !!

98. Validate Binary Search Tree

def isValidBST(self, root, lessThan = float('inf'), largerThan = float('-inf')): 
    if not root: return True # this is necessary for each isValidBST call
      # it makes sure that the following root.val valid
"""
for tree related questions:
should we always include this 'if not root' check in recursion's base case? 
how is this related with return result? if return is connected by or / and,
how is the result gonna to be different? 
"""
    if root.val <= largerThan or root.val >= lessThan: 
        return False 
    return self.isValidBST(root.left, min(lessThan, root.val), largerThan) and \\
self.isValidBST(root.right, lessThan, max(root.val, largerThan))

Python version based on inorder traversal

class Solution(object):
    def isValidBST(self, root):  
        self.res = list() 
        self.validation(root) 
        return self.res == sorted(self.res) and len(set(self.res)) == len(self.res)

    def validation(self, root): 
        if not root: 
            return 
        else: 
            self.validation(root.left) 
            self.res.append(root.val) 
            self.validation(root.right)

112. Path Sum

def hasPathSum(self, root, sum): 
    if not root: return False # this checking is needed at each call
        # return False also align with final OR return statement 
    if not root.left and not root.right and root.val == sum: 
        return True 
    sum -= root.val 
    return self.hasPathSum(root.left, sum) or self.hasPathSum(root.right, sum)

257. Binary Tree Paths

learn nested list comprehension in this example:

def binaryTreePaths(self, root): 
    if not root: 
        return [] 
    return [str(root.val) + '->' + path for kid in (root.left, root.right) if kid for path in self.binaryTreePaths(kid)] or [str(root.val)]
# NOTE: understand how nested list comprehension works !! 

Another solution (recursion):

def binaryTreePaths(self, root): 
    if not root: return [] 
    if not root.left and not root.right: 
        return [str(root.val)] 
    treepaths = [str(root.val)+'->'+path for path in self.binaryTreePaths(root.left)] 
    treepaths += [str(root.val)+'->'+path for path in self.binaryTreePaths(root.right)] 
    return treepaths

Python solutions (dfs+stack, bfs+queue, dfs recursively).

  • It is extremely important to actually draw the stack and see what's going.
  • It is also important to write out while loop and make good book keeping, when we think about such process.
  • This is a good example shows how to modify the basic DFS/BFS to complete more complicated task
  • It is particular important to notice that how we store extra information in stack/queue to complete our goal. It requires careful design to store the right information
  • In this case, the right complimentary information stored in stack for each node is the complete path from root to this node
  • The intuitive way that BFS/DFS can be tailored to complete this task is because, essentially, this task is about tree traversal. Therefore, we must be able to tailored tree traversal algorithm to handle this task.
# dfs + stack
def binaryTreePaths1(self, root):
    if not root:
        return []
    res, stack = [], [(root, "")] # basic setup
    while stack:
        node, ls = stack.pop() # for each call, initially, it will pop: node=root, ls=""
        if not node.left and not node.right:
            # means we reach leaf, and complete the path
            # append the path into res
            res.append(ls+str(node.val))
        if node.right:
            # path is not completed yet, continue to traversal deeper
            stack.append((node.right, ls+str(node.val)+"->"))
            # notice that how we store additional information in stack
            # the order of left/right doesn't matter. 
            # BFS/DFS don't matter neither. 
        if node.left:
            stack.append((node.left, ls+str(node.val)+"->"))
    return res
# bfs + queue
def binaryTreePaths2(self, root):
    if not root:
        return []
    res, queue = [], [(root, "")]
    while queue:
        node, ls = queue.pop(0)
        if not node.left and not node.right:
            res.append(ls+str(node.val))
        if node.left:
            queue.append((node.left, ls+str(node.val)+"->"))
        if node.right:
            queue.append((node.right, ls+str(node.val)+"->"))
    return res

129. Sum Root to Leaf Numbers

Python solutions (dfs+stack, bfs+queue, dfs recursively)

# Sol1: dfs + stackdef 
sumNumbers1(self, root): 
    if not root: return 0 
    stack, res = [(root, root.val)], 0 
    while stack: 
        node, value = stack.pop() 
        if node: 
            if not node.left and not node.right: 
                res += value 
            if node.right: 
                stack.append((node.right, value*10+node.right.val)) 
            if node.left: 
                stack.append((node.left, value*10+node.left.val)) 
    return res

# Sol2: bfs + queuedef
sumNumbers2(self, root): 
    if not root: return 0 
    queue, res = collections.deque([(root, root.val)]), 0 
    while queue: 
        node, value = queue.popleft() 
        if node: 
            if not node.left and not node.right: 
                res += value 
            if node.left: 
                queue.append((node.left, value*10+node.left.val)) 
            if node.right: 
                queue.append((node.right, value*10+node.right.val)) 
    return res

# Sol3: recursively 
def sumNumbers(self, root): 
    self.res = 0 
    self.dfs(root, 0) 
    return self.res

def dfs(self, root, value): 
    if root: 
        #if not root.left and not root.right: 
        # self.res += value*10 + root.val 
        self.dfs(root.left, value*10+root.val) 
        #if not root.left and not root.right: 
        # self.res += value*10 + root.val 
        self.dfs(root.right, value*10+root.val) 
        if not root.left and not root.right: 
            self.res += value*10 + root.val

100. Same Tree

def isSameTree1(self, p, q): 
    if p and q: 
        return p.val == q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) 
    else: 
        return p == q
# DFS with stack 
def isSameTree2(self, p, q): 
    stack = [(p, q)] 
    while stack: 
        node1, node2 = stack.pop() 
        if not node1 and not node2: 
            continue 
        elif None in [node1, node2]: 
            return False 
        else: 
            if node1.val != node2.val: 
                return False 
            stack.append((node1.right, node2.right)) 
            stack.append((node1.left, node2.left)) 
    return True
# BFS with queue 
def isSameTree3(self, p, q): 
    queue = [(p, q)] 
    while queue: 
        node1, node2 = queue.pop(0) 
        if not node1 and not node2: 
            continue 
        elif None in [node1, node2]: 
            return False 
        else: 
            if node1.val != node2.val: 
                return False 
            queue.append((node1.left, node2.left)) 
            queue.append((node1.right, node2.right)) 
    return True

Questions with Medium Difficulty:

114. Flatten Binary Tree to Linked List

class Solution: 
    # @param {TreeNode} root 
    # @return {void} Do not return anything, modify root in-place instead. 

    def flatten(self, root): 
        ''' 1. flatten left subtree 
            2. find left subtree's tail 
            3. set root's left to None, root's right to root'left, tail's right to root.right 
            4. flatten the original right subtree ''' 
        # escape condition 
        if not root: 
            return 
        right = root.right 
        if root.left: 
            # flatten left subtree 
            self.flatten(root.left) 
            # find the tail of left subtree 
            tail = root.left 
            while tail.right: 
                tail = tail.right 
                # left <-- None, right <-- left, tail's right <- right 
            root.left, root.right, tail.right = None, root.left, right 
        # flatten right subtree 
        self.flatten(right)

105. Construct Binary Tree from Preorder and Inorder Traversal

# it is important to write python style code !! 
def buildTree(self, preorder, inorder): 
'''
this is a very elegant solution. The order of left and right is CRUCIAL!! 
based on the idea of DFS, the recursion will go to the deepest left, 
which will pop all left side pre-order list node, 
and therefore the right side can work! 
'''
    if inorder: 
        ind = inorder.index(preorder.pop(0)) 
        root = TreeNode(inorder[ind]) 
        root.left = self.buildTree(preorder, inorder[0:ind]) 
        root.right = self.buildTree(preorder, inorder[ind+1:]) 
        return root

106. Construct Binary Tree from Inorder and Postorder Traversal

def buildTree(self, inorder, postorder): 
    if inorder: 
        ind = inorder.index(postorder.pop()) # this is the difference !! 
        root = TreeNode(inorder[ind]) 
        root.right = self.buildTree(inorder[ind+1:], postorder) # this order should be changed!! 
        root.left = self.buildTree(inorder[:ind], postorder) 
        return root

another solution, claim to use less memory

108. Convert Sorted Array to Binary Search Tree

see discussion about pass list and memory consumption here
Hello, maybe this is not perfect place to ask this question. But I am very curious whether we are allowed use a copy of lists in these recursive functions in the interview (num[:mid] and num[mid+1:] are actually creating extra sublists). It works for this problem well but for problems like Construct Binary Tree From Preorder AndInoderTraversal, when I used similar method which is very straightforwad , it works locally but OJ gives memory limit exceed. Later I solved it by dealing with indexes instead of passing copy of lists(300+ms) and improved it with the help of hash table(70+ms) but it took me long time. I hope to use the first solution if possible since we don't have much time.

def sortedArrayToBST(self, num): 
    if not num: return None 
    mid = len(num) // 2 
    root = TreeNode(num[mid]) 
    root.left = self.sortedArrayToBST(num[:mid]) 
    root.right = self.sortedArrayToBST(num[mid+1:]) 
    return root
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踪央,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖冶忱,帶你破解...
    沈念sama閱讀 222,946評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異迟蜜,居然都是意外死亡向图,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)桌吃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)朱沃,“玉大人,你說(shuō)我怎么就攤上這事茅诱《何铮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,716評(píng)論 0 364
  • 文/不壞的土叔 我叫張陵瑟俭,是天一觀(guān)的道長(zhǎng)翎卓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)摆寄,這世上最難降的妖魔是什么失暴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,222評(píng)論 1 300
  • 正文 為了忘掉前任坯门,我火速辦了婚禮,結(jié)果婚禮上逗扒,老公的妹妹穿的比我還像新娘古戴。我一直安慰自己,他們只是感情好矩肩,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,223評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布现恼。 她就那樣靜靜地躺著,像睡著了一般黍檩。 火紅的嫁衣襯著肌膚如雪述暂。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,807評(píng)論 1 314
  • 那天建炫,我揣著相機(jī)與錄音畦韭,去河邊找鬼。 笑死肛跌,一個(gè)胖子當(dāng)著我的面吹牛艺配,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播衍慎,決...
    沈念sama閱讀 41,235評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼转唉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了稳捆?” 一聲冷哼從身側(cè)響起赠法,我...
    開(kāi)封第一講書(shū)人閱讀 40,189評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎乔夯,沒(méi)想到半個(gè)月后砖织,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,712評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡末荐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,775評(píng)論 3 343
  • 正文 我和宋清朗相戀三年侧纯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甲脏。...
    茶點(diǎn)故事閱讀 40,926評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡眶熬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出块请,到底是詐尸還是另有隱情娜氏,我是刑警寧澤,帶...
    沈念sama閱讀 36,580評(píng)論 5 351
  • 正文 年R本政府宣布墩新,位于F島的核電站贸弥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏抖棘。R本人自食惡果不足惜茂腥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,259評(píng)論 3 336
  • 文/蒙蒙 一狸涌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧最岗,春花似錦帕胆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,750評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至驯用,卻和暖如春脸秽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蝴乔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,867評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工记餐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人薇正。 一個(gè)月前我還...
    沈念sama閱讀 49,368評(píng)論 3 379
  • 正文 我出身青樓片酝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親挖腰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子雕沿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,930評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • 背景 一年多以前我在知乎上答了有關(guān)LeetCode的問(wèn)題, 分享了一些自己做題目的經(jīng)驗(yàn)。 張土汪:刷leetcod...
    土汪閱讀 12,749評(píng)論 0 33
  • 小魔比較喜歡詞牌名《江城子》猴仑,所以在此獻(xiàn)丑了(雖然冬至已經(jīng)過(guò)了好久了) (上) 蒙蒙霧雨沾衣裳审轮,葉漸黃,花留香辽俗,弦...
    Python小魔閱讀 760評(píng)論 13 14
  • 我還是很喜歡與那種人前像個(gè)孩子一樣的人交流疾渣,因?yàn)樗麄兊拿恳痪湓?huà)我都可以寫(xiě)成一首詩(shī),并不是我想寫(xiě)他們榆苞,因?yàn)槟鞘自?shī)稳衬,反...
    岳遠(yuǎn)智yyz閱讀 290評(píng)論 0 1
  • 對(duì)一個(gè)地方向往的久了,那地方的輪廓就漸漸清晰起來(lái)坐漏。它甚至?xí)@入到你的夢(mèng)中,讓你不得安寢碧信。 我是個(gè)酷...
    小沈童學(xué)閱讀 864評(píng)論 2 4
  • 現(xiàn)在已經(jīng)是20號(hào)零點(diǎn)赊琳,距離我結(jié)束阿里的電話(huà)面試已經(jīng)過(guò)去了將近三個(gè)小時(shí)。我只能說(shuō)這一切來(lái)的太突然了砰碴,真的是沒(méi)有一點(diǎn)點(diǎn)...
    胖胖想努力閱讀 531評(píng)論 0 1