Binary Tree Complete Tutorial with Python 2026

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.

Binary Tree Complete Tutorial with Python 2026
Binary Tree Complete Tutorial with Python 2026

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 node

The "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.

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)

Official documentation

Adones Evangelista

Programmer & Technical Writer at PIES IT Solution

Adones Evangelista is a programmer and writer at PIES IT Solution, author of over 900 tutorials and error-fix guides at itsourcecode.com. Specializes in JavaScript, Django, Laravel, and Python error debugging covering ValueError, TypeError, AttributeError, ModuleNotFoundError, and RuntimeError, plus C/C++ and PHP capstone projects for BSIT students.

Expertise: JavaScript · Python · Django · Laravel · Error Debugging · C/C++  · View all posts by Adones Evangelista →

Leave a Comment