A binary tree is a hierarchical data structure in which every node has at most two children, conventionally called the left child and right child. It's one of the most foundational shapes in computer science — far more structures than you'd expect (binary search trees, heaps, syntax trees, decision trees) are really just binary trees with an extra rule layered on top.
Unlike a linked list, where each node points to exactly one "next" node, a binary tree node can branch in two directions at once. That branching is what makes trees good at representing hierarchical relationships — file systems, organization charts, and decision logic all map naturally onto this shape.
Basic Structure of a Binary Tree Node
A binary tree node bundles a value with references to its left and right children. Either reference can be None if that child doesn't exist.
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
# Manually building a tiny tree:
# 1
# / \
# 2 3
# /
# 4
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
print(root.left.value, root.right.value, root.left.left.value)
Output:
2 3 4
Full, Complete, and Perfect Binary Trees
These three terms get mixed up constantly, so it's worth pinning them down:
Full binary tree — every node has either 0 or 2 children, never exactly 1. Complete binary tree — every level is fully filled except possibly the last, which fills left to right with no gaps (this is the shape a heap relies on). Perfect binary tree — every internal node has exactly 2 children and every leaf sits at the same depth, so the tree is as "full" as it can possibly be.
A perfect tree is automatically full and complete, but a complete tree isn't necessarily perfect — the last level just needs to be filled left-to-right, not entirely full.
Why This Shape Matters
A plain binary tree by itself doesn't guarantee fast search — in the worst case you still have to visit every node, which is O(n). What makes binary trees powerful is the rules you can layer on top of the basic shape:
Add an ordering invariant (left child smaller, right child larger) and you get a binary search tree, with O(log n) search on average. Add a heap-order invariant (every parent is smaller — or larger — than its children) and you get a heap, ideal for always grabbing the minimum or maximum value quickly. The node structure above is the shared skeleton; everything else is a constraint you choose to enforce.
That's really the throughline across most tree-based structures in DSA: start from this same two-child node, then pick which invariant you want it to maintain.
