Breadth First Search - kjingers/Grokking-Notes GitHub Wiki

Binary Tree Level Order Traversal

def traverse(root):

  if root is None:
    return []

  result = []
  queue = deque()

  queue.append(root)

  while len(queue) > 0:
    levelSize = len(queue)
    level = []

    for _ in range(levelSize):
      node = queue.popleft()
      level.append(node.val)

      if node.left:
        queue.append(node.left)

      if node.right:
        queue.append(node.right)
    
    result.append(level)

  return result

Reverse Level Order Traversal

Same as above. Only differences is that result will be a deque(), and for each level, we will appendleft to the result.

def traverse(root):
  result = deque()
  
  queue = deque()

  if root is None:
    return

  queue.append(root)

  while queue:
    currentSize = len(queue)
    currentLevel = []
    for _ in range(currentSize):
      temp = queue.popleft()
      currentLevel.append(temp.val)
      if temp.left is not None:
        queue.append(temp.left)
      if temp.right is not None:
        queue.append(temp.right)
        
    result.appendleft(currentLevel)

  return result

Zigzag Traversal

Similar to above. We will have a leftToRight boolen to keep track of whether we are prining in order or not for the current level. Also, we will use a deque() for the level, so that we can appendleft when leftToRight is False. Then we append to the result list by: result.append(list(currentLevel))

def traverse(root):
  result = []
  queue = deque()

  if root is None:
    return None

  queue.append(root)
  leftToRight = True

  while queue:
    currentSize = len(queue)
    currentLevel = deque()
    for _ in range(currentSize):
      temp = queue.popleft()

      if temp.left:
        queue.append(temp.left)

      if temp.right:
        queue.append(temp.right)

      if leftToRight:
        currentLevel.append(temp.val)
      else:
        currentLevel.appendleft(temp.val)
      
    result.append(list(currentLevel))
    leftToRight = not leftToRight

Level Averages in a Binary Tree

Same as above except calculating averages

def find_level_averages(root):
  result = []
  queue = deque()
  if root is None:
    return
  queue.append(root)
  while queue:
    levelSize = len(queue)
    levelSum = 0.0
    for _ in range(levelSize):
      temp = queue.popleft()
      levelSum += temp.val

      if temp.left is not None:
        queue.append(temp.left)

      if temp.right is not None:
        queue.append(temp.right)

    result.append(levelSum / float(levelSize))
  return result

Minimum Depth of a Binary Tree

Minimum depth is a level where a node has no children (is a leaf node).

def find_minimum_depth(root):

  if root is None:
    return 0

  currentDepth = 1
  queue = deque()
  queue.append(root)

  while queue:
    currentSize = len(queue)
    for _ in range(currentSize):
      node = queue.popleft()

      if not node.left and not node.right:
        return currentDepth
      
      if node.left:
        queue.append(node.left)

      if node.right:
        queue.append(node.right)

    currentDepth += 1

  return currentDepth

Level Order Successor

If the current node matches the key, then simply pop the next node to get the successor.

def find_successor(root, key):
  if root is None:
    return None
  
  queue = deque()
  queue.append(root)

  while queue:
    currentSize = len(queue)
    for _ in range(currentSize):
      temp = queue.popleft()

      if temp.left:
        queue.append(temp.left)
      if temp.right:
        queue.append(temp.right)
      
      if temp.val is key:
        successor = queue.popleft()
        return successor


  return None

Connect Level Order Siblings

Very similar to above. Just keep track of previous node. Connect previous node to current node. At the start of each level, previous node is set to None, so the last node on each level has a .next that remains None.

def connect_level_order_siblings(root):
  if root is None:
    return None
  
  queue = deque()
  queue.append(root)
  while queue:
    currentSize = len(queue)
    previousNode = None
    for _ in range(currentSize):
      currentNode = queue.popleft()
      if previousNode:
        previousNode.next = currentNode
      previousNode = currentNode

      if currentNode.left:
        queue.append(currentNode.left)
      if currentNode.right:
        queue.append(currentNode.right)
      
  return

Connect All Level Order Siblings

Pretty much the same as above, except keep track of previous node between levels. Also, don't need to process levels differently, so no need to use for _ in currentSize loop.

def connect_all_siblings(root):
  if root is None:
    return

  queue = deque()
  queue.append(root)
  previousNode = None

  while queue:
    
    node = queue.popleft()
    if previousNode:
      previousNode.next = node
    
    if node.left:
      queue.append(node.left)

    if node.right:
      queue.append(node.right)

    previousNode = node

  return root

Right View of a Binary Tree

Easy. When you are at the last node for a given level, append it to result.

def tree_right_view(root):
  result = []
  queue = deque()
  queue.append(root)

  while queue:
    levelSize = len(queue)
    for i in range(levelSize):
      node = queue.popleft()

      if node.left:
        queue.append(node.left)

      if node.right:
        queue.append(node.right)

      # If this is the last (rightmost) node of the level
      if i == (levelSize - 1):
        result.append(node)
        
  return result