Finding the largest value in each tree row

finding-the-largest-value-in-each-tree-row

Recently, a friend sent me an intriguing challenge: finding the largest value in each row of a binary tree. This “tree” is a binary tree. In this post we will understand what is a binary tree and how to solve the proposed problem.

What is a binary tree?

A binary tree is a tree formed by nodes. A tree might have zero nodes, or just one. The first node of a tree is called root, and the last one (top bottom) is called leaf. A binary tree, just like a real tree, might have many branches, known as children. Children are nodes with parents, and leaves are nodes without children. Every node might have zero, one or two children.

Let’s visualize it:

binary tree

Every circle in the diagram above represents a node. A node is composed by the following attributes:

  • Value (a.k.a key)
  • Left node
  • Right node

The binary in binary tree means you can go to either left or right. If you could represent a node using Python, you would end up having the following class:

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

Finding the largest value in each tree row

Looking at the tree, we know that the largest values are: 1, 3 and 7.

We could traverse every row of the tree, save every largest value in a list and return it in the end. But finding the largest value in each row is not an easy task.

Notice we just used the word traverse. Traversing is the act of iterating over the nodes of the tree. We can do it using two techniques: depth first search (DFS) and breadth first search (BFS).

Let’s do a deep dive in both techniques:

In depth first search, you find a node in the tree and keep traversing the tree in that direction to the end (leaves). To implement this technique, we will need to use a stack

def dfs(root):
    if not root:
        return []

    stack = [root]
    values = []

    while stack:
        curr_node = stack.pop()
        values.append(curr_node.val)

        if curr_node.right:
            stack.append(curr_node.right)

        if curr_node.left:
            stack.append(curr_node.left)

    return values

Since trees might be empty (or have no nodes), we first check if the root node is present before proceeding. Otherwise we add the root to the stack and traverse the tree until this stack is not empty.

Every round of the while loop we add the value of the current node to the list and add the left and right nodes if they exist.

  • First we add root (1) value to list
  • Then we find a left node (2) and add it to the stack
  • We also found a right node (3) and add it to the stack

Ok, now it is one of the most important parts: why are we adding the right node first?

It is very common to traverse a binary tree from left to right. Since we are using a stack, we always pop the last element. Our stack looks like this in the first iteration: [right, left] If we pop from this stack, we always get the left position before right.

In breadth first search we visit every left and right node of the tree in the same iteration – this is why it has breadth in the name.

We use this technique when we want to traverse the binary tree by row.

The code is very similar to the depth first search, except we use a queue

Since we want to iterate each row, we use a queue and the popleft function – it pops the first element of the structure.
Imagine we have the following queue: [1, 10, 20, 30]. When we perform a popleft operation, the queue becomes [10, 20, 30].

from collections import deque

def bfs(root):
    if not root:
        return []

    queue = deque([root])
    values = []

    while queue:
        curr_node = queue.popleft()
        values.append(curr_node.val)

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

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

    return values
  • First we add root (1) value to list
  • Then we find a left node (2) and add it to the queue
  • We also found a right node (3) and add it to the queue
  • Then we pop left the queue and we get the node with value 2 and add it to the list, it is now [1, 2]
  • And we, again, add the left and right nodes to the queue
  • But we still have the 3 in the queue. Next iteration we will append it to the list, and so on

Solving the problem

Which algorithm you think should be used to get the largest value from each row of the tree? Think about the tree below:

binary tree

If we use depth first search, we will get all the values in a vertical way, right? In a depth first traversal, we move down the tree before considering siblings. But when finding the largest value in a row, we need to consider all nodes at the same depth before moving deeper, which aligns more with a horizontal traversal.

What about breadht first search? It seems the right technique.

Let’s implement this, but first we will reason about the steps:

  • Create a queue and add the root to it
  • While we have elements in the queue we:
    • Determine the current size of the queue, representing the number of nodes in the current row
    • And iterate N times to get all the values from the row
    • For each node in the current row, compare its value with the current maximum value (initialized as negative infinity).
    • Compare and define the new max value
    • Every time we end this iteration, we add the max value to the list of largest values

And the code is as follows:

from collections import deque

def largest_values(root):
    if not root:
        return []

    queue = deque([root])
    largest = []

    while queue:
        size = len(queue)
        max_val = float("-inf")

        for i in range(size):
            curr_node = queue.popleft()

            if curr_node.val > max_val:
                max_val = curr_node.val

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

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

        largest.append(max_val)

    return largest

For the provided example tree, this code will return the list [1, 3, 9]

Solving these challenges is a matter of reading and practicing.

Total
0
Shares
1 comment
  1. Have you ever considered creating an e-book or guest authoring on other websites?
    I have a blog based on the same subjects you discuss and
    would really like to have you share some stories/information. I
    know my visitors would appreciate your work. If you are even remotely interested,
    feel free to send me an e mail.

Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post
5-types-of-holiday-local-business-content-you-should-promote

5 Types of Holiday Local Business Content You Should Promote

Next Post
now-is-the-time-to-outsource-your-content-marketing-strategy

Now Is the Time to Outsource Your Content Marketing Strategy

Related Posts