Recently, I was working on a data analytics project for a tech startup in San Francisco, where I needed to visualize hierarchical employee data. The challenge was displaying a binary search tree structure in a readable format.
I’ve discovered there’s no single “best” way to print a BST. Different scenarios require different approaches.
In this comprehensive guide, I’ll share practical methods I use to print binary search trees in Python. Each method serves a specific purpose, from simple debugging to creating professional visualizations.
Binary Search Trees in Python
Before jumping into printing methods, let me quickly explain what we’re working with.
A Binary Search Tree (BST) is a hierarchical data structure where each node has at most two children. The left child contains smaller values than the parent, and the right child contains larger values.
Here’s the basic BST node structure I use in all my projects:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class BST:
def __init__(self):
self.root = None
def insert(self, val):
if not self.root:
self.root = TreeNode(val)
else:
self._insert_recursive(self.root, val)
def _insert_recursive(self, node, val):
if val < node.val:
if node.left is None:
node.left = TreeNode(val)
else:
self._insert_recursive(node.left, val)
else:
if node.right is None:
node.right = TreeNode(val)
else:
self._insert_recursive(node.right, val)
# Create sample BST with US city populations (in millions)
bst = BST()
cities_population = [8.4, 3.9, 2.7, 2.3, 1.6, 1.5, 1.4] # NYC, LA, Chicago, Houston, Phoenix, Philadelphia, San Antonio
for pop in cities_population:
bst.insert(pop)Method 1: In-Order Traversal Printing
In-order traversal is my go-to method when I need sorted output. This approach visits nodes in ascending order: left subtree → root → right subtree.
I use this method frequently when debugging BST implementations or when I need to verify that data is properly sorted.
def print_inorder(root):
"""Print BST nodes in ascending order"""
if root:
print_inorder(root.left)
print(f"{root.val:.1f}", end=" ")
print_inorder(root.right)
def print_inorder_with_labels(root, cities_dict=None):
"""Print BST with city labels for better understanding"""
if not cities_dict:
cities_dict = {8.4: "NYC", 3.9: "LA", 2.7: "Chicago", 2.3: "Houston",
1.6: "Phoenix", 1.5: "Philadelphia", 1.4: "San Antonio"}
def inorder_helper(node):
if node:
inorder_helper(node.left)
city_name = cities_dict.get(node.val, "Unknown")
print(f"{city_name}: {node.val:.1f}M", end=" → ")
inorder_helper(node.right)
inorder_helper(root)
print("End")
# Usage
print("In-Order Traversal (Sorted):")
print_inorder(bst.root)
print("\n")
print("In-Order with City Labels:")
print_inorder_with_labels(bst.root)I executed the above example code and added the screenshot below.

In-order traversal is a simple yet powerful way to retrieve BST data in sorted order, and adding labels makes the output more meaningful and readable.
Method 2: Pre-Order Traversal Printing
Pre-order traversal prints the root first, then left and right subtrees. I find this method useful when I need to understand the tree’s construction order or when serializing the tree.
This approach follows: root → left subtree → right subtree.
def print_preorder(root):
"""Print BST nodes in pre-order (root first)"""
if root:
print(f"{root.val:.1f}", end=" ")
print_preorder(root.left)
print_preorder(root.right)
def print_preorder_with_depth(root, depth=0, label="Root"):
"""Print pre-order traversal with depth indicators"""
cities_dict = {8.4: "NYC", 3.9: "LA", 2.7: "Chicago", 2.3: "Houston",
1.6: "Phoenix", 1.5: "Philadelphia", 1.4: "San Antonio"}
if root:
indent = " " * depth
city_name = cities_dict.get(root.val, "Unknown")
print(f"{indent}{label}: {city_name} ({root.val:.1f}M)")
if root.left:
print_preorder_with_depth(root.left, depth + 1, "L")
if root.right:
print_preorder_with_depth(root.right, depth + 1, "R")
# Usage
print("Pre-Order Traversal:")
print_preorder(bst.root)
print("\n")
print("Pre-Order with Depth Indicators:")
print_preorder_with_depth(bst.root)I executed the above example code and added the screenshot below.

Pre-order traversal reveals the tree’s construction sequence, making it ideal for understanding structure and preparing the tree for serialization.
Method 3: Level-Order (Breadth-First) Printing
Level-order traversal prints nodes level by level, from left to right. I use this method when I need to understand the tree’s structure or when creating visual representations.
This approach requires a queue data structure to track nodes at each level.
from collections import deque
def print_level_order(root):
"""Print BST nodes level by level"""
if not root:
return
queue = deque([root])
while queue:
node = queue.popleft()
print(f"{node.val:.1f}", end=" ")
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
def print_level_order_by_levels(root):
"""Print each level on a separate line"""
if not root:
return
cities_dict = {8.4: "NYC", 3.9: "LA", 2.7: "Chicago", 2.3: "Houston",
1.6: "Phoenix", 1.5: "Philadelphia", 1.4: "San Antonio"}
queue = deque([root])
level = 0
while queue:
level_size = len(queue)
print(f"Level {level}: ", end="")
for _ in range(level_size):
node = queue.popleft()
city_name = cities_dict.get(node.val, "Unknown")
print(f"{city_name}({node.val:.1f})", end=" ")
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
print() # New line after each level
level += 1
# Usage
print("Level-Order Traversal:")
print_level_order(bst.root)
print("\n")
print("Level-Order by Levels:")
print_level_order_by_levels(bst.root)I executed the above example code and added the screenshot below.

Level-order traversal captures the tree’s structure layer by layer, making it perfect for visual representations and analyzing breadth relationships.
Method 4: Tree Structure Visualization
When working on complex projects, I often need a visual representation of the tree structure. This method creates an ASCII art representation that clearly shows parent-child relationships.
def print_tree_structure(root, space=0, height=10):
"""Print tree structure with ASCII art"""
if root is None:
return
space += height
print_tree_structure(root.right, space, height)
print()
for i in range(height, space):
print(end=" ")
print(f"{root.val:.1f}")
print_tree_structure(root.left, space, height)
def print_tree_with_branches(root, prefix="", is_last=True):
"""Print tree with branch-like structure"""
cities_dict = {8.4: "NYC", 3.9: "LA", 2.7: "Chicago", 2.3: "Houston",
1.6: "Phoenix", 1.5: "Philadelphia", 1.4: "San Antonio"}
if root is not None:
city_name = cities_dict.get(root.val, "Unknown")
print(prefix + ("└── " if is_last else "├── ") + f"{city_name} ({root.val:.1f}M)")
children = []
if root.left is not None:
children.append(("left", root.left))
if root.right is not None:
children.append(("right", root.right))
for i, (child_type, child_node) in enumerate(children):
is_last_child = i == len(children) - 1
extension = " " if is_last else "│ "
print_tree_with_branches(child_node, prefix + extension, is_last_child)Create a more balanced tree for better visualization
balanced_bst = BST()
balanced_values = [2.7, 1.5, 8.4, 1.4, 2.3, 3.9, 1.6] # Chicago as root for balance
for val in balanced_values:
balanced_bst.insert(val)
print("ASCII Tree Structure:")
print_tree_structure(balanced_bst.root)
print("Tree with Branches:")
print_tree_with_branches(balanced_bst.root)
def print_detailed_tree_structure(root):
"""Print comprehensive tree structure with node relationships"""
cities_dict = {8.4: "NYC", 3.9: "LA", 2.7: "Chicago", 2.3: "Houston",
1.6: "Phoenix", 1.5: "Philadelphia", 1.4: "San Antonio"}
def get_tree_info(node, level=0, position="Root"):
if not node:
return []
city_name = cities_dict.get(node.val, "Unknown")
info = [(level, position, f"{city_name} ({node.val:.1f}M)")]
if node.left:
info.extend(get_tree_info(node.left, level + 1, "Left"))
if node.right:
info.extend(get_tree_info(node.right, level + 1, "Right"))
return info
tree_info = get_tree_info(root)
print("Detailed Tree Structure:")
for level, position, node_info in tree_info:
indent = " " * level
print(f"{indent}Level {level} ({position}): {node_info}")
# Usage
print("\nDetailed Tree Structure:")
print_detailed_tree_structure(balanced_bst.root)Tree structure visualization provides an intuitive, readable map of parent-child relationships, making complex hierarchies easier to analyze and debug.
Method 5: Pretty Print with Indentation
This is my favorite method for debugging and presentations. It creates a clean, indented representation that’s easy to read and understand the hierarchy at a glance.
def pretty_print_bst(root, indent="", last=True, is_root=True):
"""Create a beautiful indented tree representation"""
cities_dict = {8.4: "NYC", 3.9: "LA", 2.7: "Chicago", 2.3: "Houston",
1.6: "Phoenix", 1.5: "Philadelphia", 1.4: "San Antonio"}
if root is not None:
city_name = cities_dict.get(root.val, "Unknown")
if is_root:
print("Binary Search Tree Structure:")
print("=" * 40)
print(f" {city_name} ({root.val:.1f}M) [ROOT]")
is_root = False
else:
print(indent, end="")
if last:
print("└─ ", end="")
indent += " "
else:
print("├─ ", end="")
indent += "│ "
print(f" {city_name} ({root.val:.1f}M)")
# Count children to determine which is last
children = []
if root.left:
children.append(('left', root.left))
if root.right:
children.append(('right', root.right))
for i, (side, child) in enumerate(children):
is_last_child = (i == len(children) - 1)
pretty_print_bst(child, indent, is_last_child, False)
def print_bst_with_statistics(root):
"""Print BST with additional statistics"""
def calculate_stats(node):
if not node:
return 0, 0, 0 # height, nodes, sum
left_height, left_nodes, left_sum = calculate_stats(node.left)
right_height, right_nodes, right_sum = calculate_stats(node.right)
height = max(left_height, right_height) + 1
nodes = left_nodes + right_nodes + 1
total_sum = left_sum + right_sum + node.val
return height, nodes, total_sum
height, total_nodes, population_sum = calculate_stats(root)
print("\n" + "="*50)
print("BST STATISTICS")
print("="*50)
print(f" Total Cities: {total_nodes}")
print(f" Tree Height: {height}")
print(f" Total Population: {population_sum:.1f}M people")
print(f" Average Population: {population_sum/total_nodes:.2f}M people")
print("="*50)
def interactive_bst_printer(root):
"""Interactive menu for different printing options"""
cities_dict = {8.4: "NYC", 3.9: "LA", 2.7: "Chicago", 2.3: "Houston",
1.6: "Phoenix", 1.5: "Philadelphia", 1.4: "San Antonio"}
while True:
print("\n BST Printing Options:")
print("1. Pretty Print Structure")
print("2. In-Order Traversal")
print("3. Level-Order Traversal")
print("4. Tree Statistics")
print("5. Exit")
choice = input("\nSelect an option (1-5): ").strip()
if choice == '1':
pretty_print_bst(root)
elif choice == '2':
print("\nIn-Order Traversal (Sorted Cities by Population):")
print_inorder_with_labels(root, cities_dict)
elif choice == '3':
print("\nLevel-Order Traversal:")
print_level_order_by_levels(root)
elif choice == '4':
print_bst_with_statistics(root)
elif choice == '5':
print("Goodbye! ")
break
else:
print(" Invalid option. Please try again.")
pretty_print_bst(balanced_bst.root)
print_bst_with_statistics(balanced_bst.root)Pretty-printing a BST with indentation transforms raw tree data into a visually appealing, structured format, making it easier to debug, present, and understand complex hierarchies while also providing valuable statistical insights.
Conclusion
Printing binary search trees effectively is more art than science. The method you choose depends entirely on your specific use case.
I still use all five methods regularly in my projects. Each serves a unique purpose and has saved me countless hours of debugging time.
The next time you’re working with BSTs, try implementing these methods in your projects. You’ll find they make tree manipulation much more intuitive and enjoyable.
You may also read:
- Check if a Variable is NaN in Python
- Check if a Variable is Defined in Python
- Check if a Variable is Not None in Python
- Check if a Variable is a Byte String in Python

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.