4 minute read

Tree

Tree is a collection of nodes connected by directed (or undirected) edges. A tree is a nonlinear data structure, compared to arrays, linked lists, stacks, and queues which are linear data structures. A tree can be empty with no nodes or a tree is a structure consisting of one node called the root and zero or one or more subtrees.

 class TreeNode:
     def __init__(self, val=0, left=None, right=None):
         self.val = val
         self.left = left
         self.right = right

Invert Binary Tree

Given the root of a binary tree, invert the tree, and return its root

Example

Input: root = [4,2,7,1,3,6,9] Output: [4,7,2,9,6,3,1]

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

Approach 1:

I will implement a recursive method that in order to flip the values within a binary tree.

Implementation:

# recursive
class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root is None:
            return root
        right, left = self.invertTree(self.right), self.invertTree(self.left)
        root.left, root.right = right, left
        return root
        

Approach 2:

It is always a good practice trying to implement a recursive call with an iterative approach.

Deque Stack Queue
append()
appendleft() append() append()
popleft() pop()
pop() pop()

In doing so, I will use the collections.deque object, which has these useful functions:

  • append(x): add x to the right side of the deque
  • appendleft(x): add x to the left side of the deque
  • clear(): remove all elemenets from the deque leaving it with length 0
  • count(x): count the number of deque elements equal to x
  • extend(iterable): extend the right side of the deque by appending elements from the iterable argument –> also has extendleft(iterable)
  • index(x): return the position of the x –> raises ValueError if not found
  • insert(i,x): insert x into the deque at position i
  • pop(): remove and return an element from the right side of the deque –> popleft()
  • remove(value): remove the first occurrence of value –> raises ValueError if not found
  • reverse(): reverse the elements of the deque in-place

Implementation:

# iterative
class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        queue = collections.deque([root])
        while queue:
            current = queue.popleft()
            current.left, current.right = current.right, current.left
            if current.left:
                queue.append(current.left):
            if current.right:
                queue.append(current.right):
        
        return root
        

Same Tree

Given the roots of two binary trees p and q, write a function to check if they are the same or not.

Two binary trees are considered the same if they are structurally identical, and the nodes have the same value.

Approach 1:

I had to come up with the base case where p and q are both None. Then, I will implement isSameTree that recursively checks if the values of the nodes for two trees are same.

Implementation:

class Solution:
    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        if not p and not q:
            return True:
        if p and q and p.val == q.val:
            return self.isSameTree(p.left,q.left) and self.isSameTree(p.right, q.right)
        else:
            return False
        

Approach 2:

It is always a good practice trying to implement a recursive call with an iterative DFS approach.

Implementation:

class Solution:
    def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
        stack = [[p,q]]
        while stack:
            p,q = stack.pop()
            if not p and not q:
                continue
            elif p and q and p.val == q.val:
                stack.append([p.left, q.left])
                stack.append([p.right,q.right])
            else:
                return False
        return True

Maximum Depth of Binary Tree

Given the roots 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.

Approach 1:

Recursive DFS

Implementation:

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        def dfs(root):
            if not root: return 0
            left, right = dfs(root.left), dfs(root.right)
            return max(left,right) + 1
        return dfs(root)

Approach 2:

Iterative DFS

Implementation:

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if root is None:
            return 0
        stack, depth = [], 0
        stack.append((1,root))
        while stack != []:
            temp, root = stack.pop()
            if root is not None:
                depth = max(temp, depth)
                stack.append((temp+1, root.left))
                stack.append(temp+1, root.right))
        
        return depth
            

Diameter of Binary Tree

Given the root of a binary tree, return the length of the diameter of the tree.

The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root.

The length of a path between two nodes is represented by the number of edges between them.

Approach 1:

With DFS, find the maximum depth for each side recursively and update the height accordingly.

Implementation:

class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        self.ans = 0
        
        def dfs(root):
            if not root:
                return 0
            left, right = dfs(root.left), dfs(root.right)
            self.ans = max(self.ans, left + right)
            return max(left,right) + 1
        
        dfs(root)
        return self.ans

Balanced Binary Tree

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

Approach 1:

A height-balanced tree refers to a tree where the difference of depth of left subtree and the right subtree is always less than or equal to 1.

Implementation:

class Solution:
    def isBalanced(self, root: Optional[TreeNode]) -> bool:
        def dfs(root):
            if not root:
                return 0
            left, right = dfs(root.left), dfs(root.right)
            if  left == -1 or right == -1:
                return -1
            elif abs(left - right) > 1:
                return -1
            return max(left, right) +1
                
        return dfs(root) >= 0