5 Best Ways to Check If Leaf Traversal of Two Binary Trees Is the Same in Python

Rate this post

πŸ’‘ Problem Formulation: In this article, we’ll explore how to determine if two binary trees have the same leaf traversal sequence using Python. Given two binary tree root nodes, the goal is to verify if the sequence of leaves visited in a left-to-right order is identical for both trees. For instance, if both trees’ leaves result in the sequence [1, 2, 3], then they have the same leaf traversal.

Method 1: Recursive Pre-order Traversal

This method uses a recursive pre-order traversal to collect the leaves of both trees into lists. Once the leaves are collected, they can be compared for equality. It’s a simple and straightforward approach that utilizes recursion to navigate the trees.

Here’s an example:

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

def getLeaves(root, leaves):
    if root:
        getLeaves(root.left, leaves)
        if not root.left and not root.right:
            leaves.append(root.val)
        getLeaves(root.right, leaves)

def sameLeafTraversal(root1, root2):
    leaves1 = []
    leaves2 = []
    getLeaves(root1, leaves1)
    getLeaves(root2, leaves2)
    return leaves1 == leaves2

# Example Trees
tree1 = TreeNode(1)
tree1.left = TreeNode(2)
tree1.right = TreeNode(3)

tree2 = TreeNode(1)
tree2.left = TreeNode(3)
tree2.right = TreeNode(2)

print(sameLeafTraversal(tree1, tree2))
  

Output: True or False depending on the leaf traversal sequence.

This code defines a TreeNode class and a function getLeaves that collects all the leaf nodes’ values from a binary tree into a list passed by reference. The main function, sameLeafTraversal, uses this helper function to capture the leaves of both trees and then compares the lists to check if they are equal.

Method 2: Iterative Pre-order Traversal with Stack

The iterative pre-order traversal replicates the recursion stack using an explicit stack data structure. This method can sometimes be more efficient since it avoids the overhead of recursive function calls.

Here’s an example:

def getLeavesIteratively(root):
    stack, leaves = [root], []
    while stack:
        node = stack.pop()
        if node:
            if not node.left and not node.right:
                leaves.append(node.val)
            stack.extend([node.right, node.left])
    return leaves

def sameLeafTraversalIterative(root1, root2):
    return getLeavesIteratively(root1) == getLeavesIteratively(root2)

# Example Trees (same as above)
print(sameLeafTraversalIterative(tree1, tree2))
  

Output: True or False depending on the leaf traversal sequence.

The function getLeavesIteratively uses a stack to manage the nodes during traversal. Leaves are added to the leaves list when a node without children is popped. The main function, sameLeafTraversalIterative, calls this helper function for both trees and compares the results.

Method 3: Space-Optimized Comparison

This method involves a simultaneous traversal of both trees, comparing leaf nodes on-the-fly. This can be more space-efficient as it avoids storing all leaf nodes.

Here’s an example:

def isSameLeafTraversal(root1, root2):
    stack1, stack2 = [root1], [root2]
    while stack1 and stack2:
        leaf1 = leaf2 = None
        while stack1:
            node1 = stack1.pop()
            if node1:
                stack1.extend([node1.right, node1.left])
                if not node1.left and not node1.right:
                    leaf1 = node1.val
                    break
        while stack2:
            node2 = stack2.pop()
            if node2:
                stack2.extend([node2.right, node2.left])
                if not node2.left and not node2.right:
                    leaf2 = node2.val
                    break
        if leaf1 != leaf2:
            return False
    return not stack1 and not stack2

# Example Trees (same as above)
print(isSameLeafTraversal(tree1, tree2))
  

Output: True or False depending on the leaf traversal sequence.

This method uses two stacks to simultaneously traverse both trees. When a leaf node is reached in both trees, the values are compared immediately. The traversal stops if a mismatch is found or continues until all leaves have been visited.

Method 4: Generator-Based Traversal

Utilizing Python’s generator functions, this method creates a generator for leaf node traversal in both trees and compares them in a single pass. It’s a memory-efficient approach as it does not store all leaves but yields them one at a time.

Here’s an example:

def leafGenerator(root):
    stack = [root]
    while stack:
        node = stack.pop()
        if node:
            if not node.left and not node.right:
                yield node.val
            stack.extend([node.right, node.left])

def sameLeafTraversalGenerator(root1, root2):
    return all(leaf1 == leaf2 for leaf1, leaf2 in zip(leafGenerator(root1), leafGenerator(root2)))

# Example Trees (same as above)
print(sameLeafTraversalGenerator(tree1, tree2))
  

Output: True or False depending on the leaf traversal sequence.

The leafGenerator function is a generator that yields the value of each leaf node in a pre-order traversal. The main function sameLeafTraversalGenerator uses Python’s zip function to create pairs of leaf values from both trees and compares them.

Bonus One-Liner Method 5: Using itertools and All

By combining the generator traversal with itertools, this one-liner method uses the all function to check for leaf node value equality in an efficient and succinct manner.

Here’s an example:

from itertools import zip_longest

def sameLeafTraversalOneLiner(root1, root2):
    return all(l1 == l2 for l1, l2 in zip_longest(leafGenerator(root1), leafGenerator(root2), fillvalue=object()))

# Example Trees (same as above)
print(sameLeafTraversalOneLiner(tree1, tree2))
  

Output: True or False depending on the leaf traversal sequence.

This laconic version expands on the previous method by using zip_longest from the itertools module to handle trees with different numbers of leaves more gracefully, ensuring that an exhaustive comparison is made.

Summary/Discussion

  • Method 1: Recursive Pre-order Traversal. Simple to understand and implement. Potentially uses more memory for large trees.
  • Method 2: Iterative Pre-order Traversal. Avoids recursion overhead. Still requires memory to store all leaves.
  • Method 3: Space-Optimized Comparison. More space-efficient, comparing leaves as they are found without storage. Requires careful management of traversal state.
  • Method 4: Generator-Based Traversal. Memory-efficient and elegant, utilizing Python’s lazy evaluation. But less intuitive for those unfamiliar with generators.
  • Method 5: One-Liner with itertools and All. Extremely concise and combines the strengths of the generator approach with robust handling via zip_longest.