Skills for tackling LeetCode problems - Tree Depth First Search.
We can use recursion or a stack for iteration to keep track of all the parent nodes while traversing. The space complexity is O(H), where H is the maximum height of the tree.
1. Binary Tree Path Sum
Given a binary tree and a number S, find if the tree has a path from root-to-leaf such that the sum of all the node values of that path equals S.
# Time complexity O(N). Space complexity O(N). N is the total number of nodes.
# class TreeNode:
# def __init__(self, val, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
def has_path(root, S):
if root is None: return False
if root.val == S and root.left is None and root.right is None:
return True
return has_path(root.left, S - root.val) or has_path(root.right, S - root.val)
2. All Path for a Sum
Given a binary tree and a number S, find all paths from root-to-leaf such that the sum of all the node values of each path equals S.
# Time complexity O(N log N). Space complexity O(N log N).
def find_paths(root, S):
allPaths = []
DFS(root, S, [], allPaths)
return allPaths
def DFS(currentNode, S, currentPath, allPaths):
if currentNode is None: return
currentPath.append(currentNode.val)
# if sum to S, append the path to allPaths
if currentNode.val == S and currentNode.left is None and currentNode.right is None:
allPaths.append(list(currentPath))
else:
# traverse the left sub tree
DFS(currentNode.left, S - currentNode.val, currentPath, allPaths)
# traverse the right sub tree
DFS(currentNode.right, S - currentNode.val, currentPath, allPaths)
# remove the current node to backtrack
del currentPath[-1]
Note: there cannot be more than (N + 1) / 2 leaves in a binary tree, so that total root-to-leaf paths in a binary tree cannot be more than the number of leaves (N + 1) / 2. Therefore, the maximum number of elements in allPaths
will be O((N + 1) / 2) = O(N). For a balanced binary tree, the max depth or height is log N, so each path can have log N nodes in it. Therefore, the total size of the allPaths
will be O(N log N).
Similar problem: Given a binary tree, return all root-to-leaf paths:
def find_all_paths(root):
allPaths = []
DFS(root, [], allPaths)
return allPaths
def DFS(currentNode, currentPath, allPaths):
if currentNode is None: return
currentPath.append(currentNode.val)
# if reach the leaf
if currentNode.left is None and currentNode.right is None:
allPaths.append(list(currentPath))
else:
# traverse the left sub tree
DFS(currentNode.left, currentPath, allPaths)
# traverse the right sub tree
DFS(currentNode.right, currentPath, allPaths)
# remove the current node to backtrack
del currentPath[-1]
Similar problem: Given a binary tree, find the root-to-leaf pat with the maximum sum.
def find_all_paths(root):
allPaths = {}
DFS(root, 0, [], allPaths)
# there might be multiple paths with the max sum, but can only return one of them
return allPaths[max(allPaths)]
def DFS(currentNode, SUM, currentPath, allPaths):
if currentNode is None: return
currentPath.append(currentNode.val)
SUM += currentNode.val
# if reach the leaf
if currentNode.left is None and currentNode.right is None:
allPaths[SUM] = list(currentPath)
else:
# traverse the left sub tree
DFS(currentNode.left, SUM, currentPath, allPaths)
# traverse the right sub tree
DFS(currentNode.right, SUM, currentPath, allPaths)
# remove the current node to backtrack
SUM -= currentPath[-1]
del currentPath[-1]
3. Sum of Path Numbers
Given a binary tree where each node can only have a digit (0-9) value, each root-to-leaf path will represent a number. Find the total sum of all the numbers represented by all paths.
# Time complexity O(N). Space complexity O(N).
def find_sum_of_path_numbers(root):
return find_root_to_leaf_path_numbers(root, 0)
def find_root_to_leaf_path_numbers(currentNode, pathSum):
if currentNode is None: return 0
pathSum = 10 * pathSum + currentNode.val
if currentNode.left is None and currentNode.right is None:
return pathSum
return find_root_to_leaf_path_numbers(currentNode.left, pathSum) + find_root_to_leaf_path_numbers(currentNode.right, pathSum)
4. Path with Given Sequence
Given a binary tree and a number sequence, find if the sequence if present as a root-to-leaf path in the given tree.
# Time complexity O(N). Space complexity O(N).
def find_path(root, sequence):
if not root:
return len(sequence) == 0
return find_path_recursive(root, sequence, 0)
def find_path_recursive(currentNode, sequence, sequenceIndex):
if currentNode is None: return False
seqlength = len(sequence)
# if exceed the end of the seq, or does not match
if sequenceIndex >= seqlength or currentNode.val != sequence[sequenceIndex]:
return False
# reach the end of the sequence and the leaf of the tree
if currentNode.right is None and currentNode.right is None and sequenceIndex == seqlength -1:
return True
# return True of any side returns True
return find_path_recursive(currentNode.left, sequence, sequenceIndex + 1) or find_path_recursive(currentNode.right, sequence, sequenceIndex + 1)
5. Count Path for a Sum
Given a binary tree and a number S, find all paths in the tree such that the sum of all the node values of each path equals S. Please node that the paths can start or end at any node but all paths must follow direction from parent to child.
# Time complexity O(N^2) in the worst case, O(N log N) if the tree is balanced (the height of a balanced tree is log N).
# Space complexity O(N).
def count_paths(root, S):
return count_paths_recursive(root, S, [])
def count_paths_recursive(currentNode, S, currentPath):
if currentNode is None:
return 0
currentPath.append(currentNode.val)
# A path can start or end at any node
Count, Sum = 0,0
for i in range(len(currentPath) - 1, -1, -1):
Sum += currentPath[i]
if Sum == S: Count += 1
# traverse the left sub tree
Count += count_paths_recursive(currentNode.left, S, currentPath)
# traverse the right sub tree
Count += count_paths_recursive(currentNode.right, S, currentPath)
del currentPath[-1]
return Count