“Print a binary search tree” can mean two different things. If you want the stored values as a flat list, use a traversal, and an inorder traversal gives you the keys in sorted order. If you want to see how the nodes hang off each other, you need a tree-shaped text layout that shows left and right branches. The code below does both, using plain Python 3 with no third-party packages.
Decide what “print” should show
Each option answers a different question. Pick the row that matches your goal before you write any output code.
| Output style | Visit order | Result for the sample tree (5, 3, 8, 1, 4, 7, 9) | Use it when |
|---|---|---|---|
| Inorder | left, node, right | 1 3 4 5 7 8 9 | You want the stored values in ascending order |
| Preorder | node, left, right | 5 3 1 4 8 7 9 | You want the root listed before its subtrees |
| Postorder | left, right, node | 1 4 3 7 9 8 5 | You want each node listed after both of its subtrees |
| Level-order | one depth at a time, left to right | 5 3 8 1 4 7 9 | You want the nodes grouped by depth |
| Tree-shaped text | rotated view, right branches above, left branches below | Shown in the tree-shaped section below | You want to see parent-child relationships at a glance |
A flat traversal is a sequence, so it hides the shape. Inorder output in particular is sorted whether the tree is balanced or badly skewed, which means it tells you nothing about how the tree is built. Use the tree-shaped view when the structure is what you are debugging.
Build the sample tree
Every example uses the same node class and insertion function. The insertion rule is the one place where duplicate keys are decided: a key equal to an existing node goes into the right subtree, so repeated values remain in the tree and appear next to each other in inorder output. If you want duplicates rejected instead, return the existing node when key == root.key.
Free tools Windows power users keep installed
One-click scans. No signup required.
#1 Best Overall
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def insert(root, key):
if root is None:
return Node(key)
if key < root.key:
root.left = insert(root.left, key)
else: # equal keys go right, so duplicates are kept
root.right = insert(root.right, key)
return root
root = None
for k in [5, 3, 8, 1, 4, 7, 9]:
root = insert(root, k)
Flat traversals
All four traversals share the same recursive pattern. They differ only in when the current node is appended to the output list. The sample results in the table above come from these functions.
Inorder: sorted values
Visit the left subtree, record the current key, then visit the right subtree. For a binary search tree, this is the sorted sequence of keys. The algo-py documentation’s Binary Search Tree page describes this property for BSTs.
Rank #2
def inorder(node, out=None):
if out is None:
out = []
if node is not None:
inorder(node.left, out)
out.append(node.key)
inorder(node.right, out)
return out
print(" ".join(map(str, inorder(root)))) # 1 3 4 5 7 8 9
Preorder: root first
Record the current key before visiting either subtree. This is useful when the root should appear first in your output, for example when you are logging a tree’s top-level structure before its contents.
def preorder(node, out=None):
if out is None:
out = []
if node is not None:
out.append(node.key)
preorder(node.left, out)
preorder(node.right, out)
return out
print(" ".join(map(str, preorder(root)))) # 5 3 1 4 8 7 9
Postorder: children first
Record the current key after both subtrees. The root always comes last, which is the order you want when a parent depends on results computed from its children.
def postorder(node, out=None):
if out is None:
out = []
if node is not None:
postorder(node.left, out)
postorder(node.right, out)
out.append(node.key)
return out
print(" ".join(map(str, postorder(root)))) # 1 4 3 7 9 8 5
Level-order: one depth at a time
Level-order traversal is not recursive. It uses a queue: take a node from the front, record it, and enqueue its children. The result groups nodes by depth, which is the closest a flat list gets to showing the tree’s layers.
from collections import deque
def level_order(root):
result = []
queue = deque([root]) if root is not None else deque()
while queue:
node = queue.popleft()
result.append(node.key)
if node.left is not None:
queue.append(node.left)
if node.right is not None:
queue.append(node.right)
return result
print(" ".join(map(str, level_order(root)))) # 5 3 8 1 4 7 9
Tree-shaped text display
This layout turns the tree sideways. The root sits at the left margin, right children appear above it and left children below, and each level of depth moves four spaces to the right. Reading the output from top to bottom gives you the tree rotated 90 degrees counterclockwise; the convention is a choice, and you can flip it by swapping the two calls. The labels R: and L: mark which side of its parent each node sits on.
def print_tree(node, indent="", label=""):
if node is None:
return
print_tree(node.right, indent + " ", "R: ")
print(indent + label + str(node.key))
print_tree(node.left, indent + " ", "L: ")
print_tree(root)
For the sample tree, the output is:
R: 9
R: 8
L: 7
5
R: 4
L: 3
L: 1
Read it this way: 8 is the right child of 5, 9 is the right child of 8, 7 is the left child of 8, and so on. Because the function recurses through every node, each line’s indentation tells you its depth and each label tells you its side.
Empty trees and deep trees
- Empty tree.
print_tree(None)prints nothing, andinorder(None)returns an empty list. Neither output is wrong, but a blank screen is easy to mistake for a failed call. If you want a visible marker, check forNoneat the call site and print something such as(empty tree). - Deep or skewed trees. Inserting already-sorted keys produces a chain where every node is a right child. All of the recursive functions above then recurse once per level, and Python’s default recursion limit (commonly 1000) can be reached on a long chain. Print a tree like this with an iterative version, or raise the limit with
sys.setrecursionlimitonly if you understand the stack cost. - Wide output. The indentation grows by four spaces per level, so a deep tree can exceed the width of a terminal and wrap badly. For large trees, print level-order values instead, or write the text to a file.
Which output to use
- Want the values sorted? Use inorder.
- Want the root listed first? Use preorder.
- Want children processed before their parent? Use postorder.
- Want nodes grouped by depth? Use level-order.
- Want to see the shape and parent-child links? Use the tree-shaped display.
For further reading on these traversals in Python, the book Data Structures and Algorithms in Python covers the same material; confirm the edition and current availability before purchasing.
Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




