Binary Tree Python

[2]:
import IPython; IPython.display.HTML('''<script>code_show=true; function code_toggle() { if (code_show){ $('div.nbinput').show(); } else { $('div.nbinput').hide(); } code_show = !code_show} $( document ).ready(code_toggle);</script><form action="javascript:code_toggle()"><input type="submit" value="Click here to toggle on/off the raw code."></form>''')
[2]:

104. Maximum Depth of Binary Tree

Easy

Given the root of a binary tree, return its maximum depth.

A binary tree’s maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.

Example 1:

image104.png

Input: root = [3,9,20,null,null,15,7]
Output: 3

Constraints:

The number of nodes in the tree is in the range [0, 10^4].
-100 <= Node.val <= 100
[1]:
# recursive

def maxDepth(root):
    if root:
        return 1 + max(maxDepth(root.left), maxDepth(root.right))
    else:
        return 0
[2]:
# BFS, working but slow

def maxDepth(root):
    q = deque()
    if root:
        q.append(root)

    depth = 0
    while q:
        for _ in range(len(q)):
            node = q.popleft()
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)

        depth += 1
    return depth

110. Balanced Binary Tree

Easy

Given a binary tree, determine if it is height-balanced.

For this problem, a height-balanced binary tree is defined as:

a binary tree in which the left and right subtrees of every node differ in height by no more than 1.

Example 1:

image110.png

Input: root = [3,9,20,null,null,15,7]
Output: true

Constraints:

The number of nodes in the tree is in the range [0, 5000].
-10^4 <= Node.val <= 10^4
[3]:
# 遞迴的過程中需要傳其它資訊(子樹深度)。寫一個遞迴函數 depth_balance 輸出完整資訊,但最後 isBalanced 只回傳需要的部份

def isBalanced(root):
    def depth_balance(root):
        '''return (depth, isBalanced)'''
        if root:
            depth_l, balanced_l = depth_balance(root.left)
            depth_r, balanced_r = depth_balance(root.right)

            return 1 + max(depth_l, depth_r), balanced_l and balanced_r and abs(depth_l - depth_r) < 2
        else:
            return 0, True

    return depth_balance(root)[1]

124. Binary Tree Maximum Path Sum

Hard

A path in a binary tree is a sequence of nodes where each pair of adjacent nodes in the sequence has an edge connecting them. A node can only appear in the sequence at most once. Note that the path does not need to pass through the root.

The path sum of a path is the sum of the node’s values in the path.

Given the root of a binary tree, return the maximum path sum of any path.

Example 2:

image124.png

Input: root = [-10,9,20,null,null,15,7]
Output: 42
Explanation: The optimal path is 15 -> 20 -> 7 with a path sum of 15 + 20 + 7 = 42.

Constraints:

The number of nodes in the tree is in the range [1, 3*10^4].
-1000 <= Node.val <= 1000
[ ]:
# rsum 本身一定要出現在 mps return 的 max 裡不然 [0] 不會過

import math

def maxPathSum(root):
    def mps(root):
        '''
        return two sums:

        1. the max path sum
        2. the restricted max path sum where one end of the path is the root
        '''
        if root:
            sum_l, rsum_l = mps(root.left)
            sum_r, rsum_r = mps(root.right)
            rsum = max(root.val, root.val + rsum_l, root.val + rsum_r)

            return max(sum_l, sum_r, rsum, root.val + rsum_l + rsum_r), rsum
        else:
            return -math.inf, -math.inf

    return mps(root)[0]

236. Lowest Common Ancestor of a Binary Tree

Medium

Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.

According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”

Example 2:

image236.png

Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
Output: 5
Explanation: The LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition.

Constraints:

The number of nodes in the tree is in the range [2, 10^5].
-10^9 <= Node.val <= 10^9
All Node.val are unique.
p != q
p and q will exist in the tree.
[1]:
# LeetCode 上最快解答:DFS 從下向上(divide and conquer 先遞迴傳回結果再合併)

def lowestCommonAncestor(root, p, q):
    if root in [p, q, None]:
        return root

    left = lowestCommonAncestor(root.left, p, q)
    right = lowestCommonAncestor(root.right, p, q)

    if left and right:
        return root
    elif left:
        return left
    else:
        return right

102. Binary Tree Level Order Traversal

Medium

Given the root of a binary tree, return the level order traversal of its nodes’ values. (i.e., from left to right, level by level).

Example 1:

image102.png

Input: root = [3,9,20,null,null,15,7]
Output: [[3],[9,20],[15,7]]

Constraints:

The number of nodes in the tree is in the range [0, 2000].
-1000 <= Node.val <= 1000
[ ]:
# BFS

from collections import deque

def levelOrder(root):
    q = deque()
    if root:
        q.append(root)

    res = []
    while q:
        level = []
        for _ in range(len(q)):
            node = q.popleft()
            level.append(node.val)
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)

        res.append(level)
    return res

107. Binary Tree Level Order Traversal II

Medium

Given the root of a binary tree, return the bottom-up level order traversal of its nodes’ values. (i.e., from left to right, level by level from leaf to root).

Example 1:

image107.png

Input: root = [3,9,20,null,null,15,7]
Output: [[15,7],[9,20],[3]]

Constraints:

The number of nodes in the tree is in the range [0, 2000].
-1000 <= Node.val <= 1000
[ ]:
# BFS 解答跟上面那題一模一樣只是 return res[::-1]。LeetCode 上速度前幾名的 BFS 解答都是這樣做的
# 或者把 res.append(level) 那行改成 res.insert(0, level),但理論上會慢一點

# 這題也可以用 recursion

103. Binary Tree Zigzag Level Order Traversal

Medium

Given the root of a binary tree, return the zigzag level order traversal of its nodes’ values. (i.e., from left to right, then right to left for the next level and alternate between).

Example 1:

image103.png

Input: root = [3,9,20,null,null,15,7]
Output: [[3],[20,9],[15,7]]

Constraints:

The number of nodes in the tree is in the range [0, 2000].
-100 <= Node.val <= 100
[ ]:
# 作弊的解答同 107 不過在 return 前改 reverse res 裡需要 reverse 的
# 下面是不作弊的方法:每層裡 pop 和 popleft 穿插著用。看 binary tree python(主標題 link)
# 在 pop 的一層裡要 push 下一層的 node 就一定要用 appendleft 才不會一 push 就被 pop 了

from collections import deque

def zigzagLevelOrder(root):
    q = deque()
    if root:
        q.append(root)

    from_left = True
    res = []
    while q:
        level = []
        for _ in range(len(q)):
            if from_left:
                node = q.popleft()
                level.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
            else:
                node = q.pop()
                level.append(node.val)
                if node.right:
                    q.appendleft(node.right)
                if node.left:
                    q.appendleft(node.left)

        from_left = not from_left
        res.append(level)
    return res

98. Validate Binary Search Tree

Medium

Given the root of a binary tree, determine if it is a valid binary search tree (BST).

A valid BST is defined as follows:

  • The left subtree of a node contains only nodes with keys less than the node’s key.

  • The right subtree of a node contains only nodes with keys greater than the node’s key.

  • Both the left and right subtrees must also be binary search trees.

Example 1:

image98.png

Input: root = [2,1,3]
Output: true

Constraints:

The number of nodes in the tree is in the range [1, 10^4].
-2^31 <= Node.val <= 2^31 - 1
[1]:
# 檢查 inorder 的結果是否 sorted

def isValidBST(root):
    lst = inorder(root)
    return all([lst[i] < lst[i+1] for i in range(len(lst)-1)])

def inorder(root):
    if not root:
        return []
    else:
        return inorder(root.left) + [root.val] + inorder(root.right)
[ ]:
# From LeetCode

def isValidBST(root):
    def validate(node, low=-math.inf, high=math.inf):
        '''
        verify if node is a valid BST taking values in (low, high)
        '''
        if not node:
            return True
        if node.val <= low or node.val >= high:
            return False

        return (validate(node.right, node.val, high) and validate(node.left, low, node.val))

    return validate(root)

701. Insert into a Binary Search Tree

Medium

You are given the root node of a binary search tree (BST) and a value to insert into the tree. Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST.

Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. You can return any of them.

Example 1:

image701.png

Input: root = [4,2,7,1,3], val = 5
Output: [4,2,7,1,3,5]

Constraints:

The number of nodes in the tree will be in the range [0, 10^4].
-10^8 <= Node.val <= 10^8
All the values Node.val are unique.
-10^8 <= val <= 10^8
It's guaranteed that val does not exist in the original BST.
[ ]:
# naively move to the right place and add a node

def insertIntoBST(root, val):
    if not root:
        return TreeNode(val)

    cur = root
    while cur:
        prev = cur
        if cur.val > val:
            cur = cur.left
        else:
            cur = cur.right

    if prev.val > val:
        prev.left = TreeNode(val)
    else:
        prev.right = TreeNode(val)

    return root
[ ]:
# clean recursive solution from LeetCode

def insertIntoBST(root, val):
    if not root:
        return TreeNode(val)
    if val < root.val:
        root.left = insertIntoBST(root.left, val)
    else:
        root.right = insertIntoBST(root.right, val)
    return root