LC 222 & LC 110 & LC 257


Solution to LeetCode 222 Count Complete Tree Nodes, LeetCode 110 Balanced Binary Tree, and LeetCode 257 Binary Tree Paths.

LeetCode 222

Count Complete Tree Nodes (Medium) [link]

1] BFS. Level-order traversal. Iterative solution.

Time complexity O(n). Space complexity O(n).

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        
        que = deque([root])
        cnt = 0
        if not root:
            return cnt
            
        while que:
            size = len(que)
            for i in range(size):
                cur = que.popleft()
                cnt += 1 
                if cur.left:
                    que.append(cur.left)
                if cur.right:
                    que.append(cur.right)
        return cnt

2] DFS. Recursive solution.

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        
        def dfs(node):
            if not node:
                return 0
            leftnum = dfs(node.left)
            rightnum = dfs(node.right)
            return leftnum + rightnum + 1
        
        return dfs(root)
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def countNodes(self, root: Optional[TreeNode]) -> int:
        if not root: return 0
        return self.countNodes(root.left)+self.countNodes(root.right)+1

LeetCode 110

Balanced Binary Tree. (Easy) [link]

Note the difference between term “depth” and “height” of tree node. ‘depth’ is measured from the root while ‘height’ is measured from leaf.

**Bottom up. Recursion solution. Post-order traversal. **

The recursion function returns the max height (i.e. max(left_height + right_height)+1) of the currently root if the height difference < 2. It returns -1 if the height difference is >= 2.

The recursion terminates and returns 0 when it reaches to leaf, or terminates and returns -1 when left or right tree returns -1.

Time complexity O(n). Space complexity O(n).

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        if not root:
            return True

        def get_height(node):
            if not node:
                return 0

            leftHeight = get_height(node.left)
            rightHeight = get_height(node.right)
            if leftHeight == -1: # left
                return -1
            if rightHeight == -1: # right
                return -1
            if abs(leftHeight - rightHeight) > 1: # root
                return -1
            else:
                return 1 + max(leftHeight, rightHeight)
        
        return get_height(root) != -1

Iterative method is feasible but not suitable.

LeetCode 257

Binary Tree Paths (Easy) [link]

1] Pre-order traversal. Backtracking. Recursion.

Time complexity O(n^2). It takes O(n) to traverse all the node once and takes O(n) to construct path whenever traverse to new node.

Space complexity O(n^2). The space taken by path is O(1 + 2 + …+ n) = O(n^2).

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        path = ''
        result = []
        if not root: return result
        self.traversal(root, path, result)
        return result

    def traversal(self, root, path, result):
        path += str(root.val)
        if not root.left and not root.right:
            result.append(path)
        if root.left:
            self.traversal(root.left, path + '->', result)
        if root.right:
            self.traversal(root.right, path + '->', result)

2] Pre-order traversal. Iterative method using Stack. One stack stores the current node, the other stack stores the current path.

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        stack = [root]
        cur_path = [str(root.val)]
        result = []
        while stack:
            cur = stack.pop()
            path = cur_path.pop()
            if not cur.left and not cur.right:
                result.append(path)
            if cur.right:
                stack.append(cur.right)
                cur_path.append(path+'->'+ str(cur.right.val))
            if cur.left:
                stack.append(cur.left)
                cur_path.append(path+'->'+ str(cur.left.val))
        return result

3] Pre-order traversal. Iterative method using Queue. One queue stores the current node, the other queue stores the current path.

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:
        que = deque()
        que.append(root)
        cur_path = deque()
        cur_path.append(str(root.val))
        result = []
        while que:
            cur = que.popleft()
            path = cur_path.popleft()
            if not cur.left and not cur.right:
                result.append(path)
            if cur.left:
                que.append(cur.left)
                cur_path.append(path+'->'+ str(cur.left.val))
            if cur.right:
                que.append(cur.right)
                cur_path.append(path+'->'+ str(cur.right.val))
        return result

Note that the order of appending left node and right node does not matter. But if we strictly follow the pre-order traversal (root -> left -> right). When using stack, append right child first. When using queue, append left child first.


  TOC