Binary trees are the data structure that unlocks about 40 percent of LeetCode Medium problems. Master traversal patterns and BFS/DFS becomes automatic. This 2026 tutorial walks you through building a binary tree in Python, all four traversal orders (inorder, preorder, postorder, level-order), Binary Search Tree operations, and the common interview problems that test each pattern.

Quick 2026 verdict
A binary tree is a node with up to 2 children. A Binary Search Tree (BST) adds the rule “left subtree values < node value < right subtree values”. Traversals: inorder gives sorted output on BST, preorder is used for serialization, postorder for cleanup / deletion, level-order (BFS) for shortest-path and “print by depth” problems. Master these 4 patterns and half of tree interview questions solve themselves.
Building a binary tree in Python
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
# Build:
# 4
# / \
# 2 6
# / \ / \
# 1 3 5 7
root = Node(4)
root.left = Node(2)
root.right = Node(6)
root.left.left = Node(1)
root.left.right = Node(3)
root.right.left = Node(5)
root.right.right = Node(7)That is it. A tree in Python is just a class with left and right pointers. Everything else (insertion, search, traversal) is a function that operates on the root node.
The 4 traversal orders
Inorder (left, root, right): visits nodes in sorted order for a BST.
def inorder(node, result=None):
if result is None: result = []
if node is None: return result
inorder(node.left, result)
result.append(node.value)
inorder(node.right, result)
return result
print(inorder(root)) # [1, 2, 3, 4, 5, 6, 7]Preorder (root, left, right): used for tree serialization + deep copy.
def preorder(node, result=None):
if result is None: result = []
if node is None: return result
result.append(node.value)
preorder(node.left, result)
preorder(node.right, result)
return result
print(preorder(root)) # [4, 2, 1, 3, 6, 5, 7]Postorder (left, right, root): used for tree deletion + expression evaluation.
def postorder(node, result=None):
if result is None: result = []
if node is None: return result
postorder(node.left, result)
postorder(node.right, result)
result.append(node.value)
return result
print(postorder(root)) # [1, 3, 2, 5, 7, 6, 4]Level-order (BFS, top to bottom): the interview favorite for “print tree layer by layer” problems.
from collections import deque
def level_order(root):
if root is None: return []
result = []
queue = deque([root])
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.value)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(level)
return result
print(level_order(root)) # [[4], [2, 6], [1, 3, 5, 7]]Binary Search Tree operations
A BST maintains the invariant: for every node, all left descendants are less than it, all right descendants are greater. This gives you O(log n) search, insert, and delete on a balanced tree.
def bst_insert(root, value):
if root is None: return Node(value)
if value < root.value:
root.left = bst_insert(root.left, value)
else:
root.right = bst_insert(root.right, value)
return root
def bst_search(root, value):
if root is None or root.value == value:
return root
if value < root.value:
return bst_search(root.left, value)
return bst_search(root.right, value)Warning: a BST is only O(log n) if it stays balanced. Insert 1, 2, 3, 4, 5 in that order and your "tree" is a linked list with O(n) operations. For guaranteed balance use AVL trees or Red-Black trees (both are self-balancing BSTs).
Common tree properties (all one-liners)
def max_depth(node):
if node is None: return 0
return 1 + max(max_depth(node.left), max_depth(node.right))
def count_nodes(node):
if node is None: return 0
return 1 + count_nodes(node.left) + count_nodes(node.right)
def is_symmetric(node):
def mirror(a, b):
if a is None and b is None: return True
if a is None or b is None: return False
return a.value == b.value and mirror(a.left, b.right) and mirror(a.right, b.left)
return node is None or mirror(node.left, node.right)
def invert(node):
if node is None: return None
node.left, node.right = invert(node.right), invert(node.left)
return nodeThe "invert binary tree" question is famously the interview question that Max Howell (creator of Homebrew) failed at Google. Now you know it. 4 lines.
The 5 most-asked tree interview problems
- Maximum depth of binary tree. Recursion, one line. Shown above.
- Symmetric tree. Compare left subtree to right subtree mirror. Shown above.
- Lowest Common Ancestor (LCA). Recurse; if current node is one of the targets OR both subtrees return non-null, current is the LCA.
- Serialize / Deserialize binary tree. Preorder traversal with null markers. Reverse for deserialization.
- Path sum. DFS with running sum, check on leaf nodes.
def lowest_common_ancestor(root, p, q):
if root is None or root == p or root == q: return root
left = lowest_common_ancestor(root.left, p, q)
right = lowest_common_ancestor(root.right, p, q)
if left and right: return root
return left or right
def has_path_sum(root, target):
if root is None: return False
if root.left is None and root.right is None: return root.value == target
remaining = target - root.value
return has_path_sum(root.left, remaining) or has_path_sum(root.right, remaining)DFS vs BFS: which to use when
- DFS (recursion or stack): "process each subtree completely before moving on". Good for path-finding, tree properties (depth, count, sum). Uses O(depth) stack space.
- BFS (queue): "process each level before moving deeper". Good for shortest-path in unweighted graphs, level-order printing, "find the closest node with property X". Uses O(width) queue space.
- Rule of thumb: if the problem says "level" or "shortest", think BFS. Otherwise start with DFS (usually shorter code).
Common mistakes on tree problems
- Forgetting the null check. Recursive tree functions must return early if node is None. Forgetting this crashes with AttributeError.
- Confusing BST with binary tree. A binary tree is any tree with 2 children max. Only BST has the ordering property. Interview problem "search in binary tree" is O(n); "search in BST" is O(log n) if balanced.
- Assuming balanced. Interview problems rarely guarantee balance. Always state complexity as O(n) worst case unless the problem specifies AVL or Red-Black.
- Iterative traversal without deque. Using list.pop(0) as a queue is O(n) per pop. Use collections.deque for O(1).
- Modifying the tree while iterating. Same rule as HashMap: collect nodes first, then modify.
Official documentation
Frequently Asked Questions
What is the difference between binary tree and BST?
A binary tree is any tree where each node has at most 2 children (no ordering constraint). A Binary Search Tree adds the rule "left subtree values < current value < right subtree values". BST gives you O(log n) search / insert / delete if balanced; a generic binary tree is O(n) for those operations.
When should I use iterative traversal instead of recursive?
When the tree could be very deep (more than Python's 1,000-frame limit) OR when interviewer explicitly asks for iterative. Recursive is 3-5 lines shorter and easier to read. Interviewer preference varies; state both approaches out loud, code the recursive one first.
How do I traverse a tree without recursion?
DFS iteratively: use a stack. Push root, then pop and push children until stack is empty. BFS iteratively: use a queue (collections.deque). Push root, pop and push children until queue is empty. In Python, `stack.pop()` gives DFS behavior, `queue.popleft()` gives BFS.
What is a balanced tree?
A tree is balanced if for every node the heights of left and right subtrees differ by at most 1. Balance keeps operations O(log n). AVL trees strictly enforce this. Red-Black trees allow slight imbalance for faster inserts. Python's sortedcontainers.SortedList uses a variation that stays balanced without explicit rotations.
How is a binary heap different from a BST?
A binary heap maintains the "heap property" (parent smaller than children in a min-heap) but has no left/right ordering. It is stored in an array, not with pointer nodes. Heaps give O(1) min access and O(log n) insert / delete-min. Use heap when you need min/max quickly (priority queue). Use BST when you need sorted iteration.
Do I need to know AVL and Red-Black trees for interviews?
Conceptually yes (know they exist, know they are self-balancing BSTs, know Java TreeMap uses Red-Black). No interviewer will ask you to code an AVL rotation from memory. If your BST answer works and complexity is analyzed correctly, you get credit.
Related DSA + Interview tutorials
- Big-O Notation Complete Guide for Beginners 2026
- Recursion Explained with Real Examples 2026
- HashMap Deep Dive 2026 Python + Java + JavaScript
- Graph Algorithms Complete 2026 Guide (coming this week)