Binary Trees
A binary tree is a hierarchical data structure where each node has at most two children (left and right). Trees model hierarchical relationships and enable efficient searching, sorting, and expression parsing.
Binary Tree Terminology
A binary tree is a tree where each node has at most two children. Key terminology:
- Root — The topmost node (no parent) - Leaf — A node with no children - Height — The length of the longest path from root to a leaf - Depth — The distance from the root to a given node - Level — All nodes at the same depth - Subtree — A node and all its descendants
Special types: - Full Binary Tree — Every node has 0 or 2 children - Complete Binary Tree — All levels filled except possibly the last, which is filled left to right - Perfect Binary Tree — All internal nodes have 2 children and all leaves are at the same level - Balanced Binary Tree — Height is O(log n)
Tree Traversals
There are four fundamental ways to traverse a binary tree:
- Inorder (Left, Root, Right) — For BSTs, produces elements in sorted order.
- Preorder (Root, Left, Right) — Used to copy/serialize a tree.
- Postorder (Left, Right, Root) — Used to delete a tree or evaluate expression trees.
- Level-order (BFS) — Visits nodes level by level. Uses a queue.
Implementing Binary Tree Traversals
Both recursive and iterative implementations of all four traversals:
from collections import deque
from typing import Optional, List
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
# ---- Recursive Traversals ----
def inorder(root: Optional[TreeNode]) -> List[int]:
if not root:
return []
return inorder(root.left) + [root.val] + inorder(root.right)
def preorder(root: Optional[TreeNode]) -> List[int]:
if not root:
return []
return [root.val] + preorder(root.left) + preorder(root.right)
def postorder(root: Optional[TreeNode]) -> List[int]:
if not root:
return []
return postorder(root.left) + postorder(root.right) + [root.val]
# ---- Level-Order (BFS) ----
def level_order(root: Optional[TreeNode]) -> List[List[int]]:
if not root:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue)
level = []
for _ in range(level_size):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
# ---- Iterative Inorder (using explicit stack) ----
def inorder_iterative(root: Optional[TreeNode]) -> List[int]:
result = []
stack = []
current = root
while current or stack:
while current:
stack.append(current)
current = current.left
current = stack.pop()
result.append(current.val)
current = current.right
return result
# Build test tree: 4
# / # 2 6
# / / # 1 3 5 7
root = TreeNode(4,
TreeNode(2, TreeNode(1), TreeNode(3)),
TreeNode(6, TreeNode(5), TreeNode(7))
)
print("Inorder: ", inorder(root)) # [1,2,3,4,5,6,7]
print("Preorder: ", preorder(root)) # [4,2,1,3,6,5,7]
print("Postorder: ", postorder(root)) # [1,3,2,5,7,6,4]
print("Level-order:", level_order(root)) # [[4],[2,6],[1,3,5,7]]Tree Height, Diameter, and Path Sum
Common tree problems solved with recursion:
# ---- Height of Binary Tree ----
def height(root):
if not root:
return 0
return 1 + max(height(root.left), height(root.right))
# ---- Diameter (longest path between any two nodes) ----
def diameter(root):
max_diameter = [0]
def depth(node):
if not node:
return 0
left = depth(node.left)
right = depth(node.right)
max_diameter[0] = max(max_diameter[0], left + right)
return 1 + max(left, right)
depth(root)
return max_diameter[0]
# ---- Check if path sum exists ----
def has_path_sum(root, target):
if not root:
return False
if not root.left and not root.right: # Leaf node
return root.val == target
return (has_path_sum(root.left, target - root.val) or
has_path_sum(root.right, target - root.val))
print("Height:", height(root)) # 3
print("Diameter:", diameter(root)) # 4 (path: 1-2-4-6-7 or 3-2-4-6-5)Key Takeaways
- Binary trees have at most two children per node; height of a balanced tree is O(log n).
- Inorder traversal of a BST produces sorted output; preorder is used for serialization.
- Level-order traversal uses a queue (BFS); depth-first traversals use recursion or a stack.
- Most tree problems are solved elegantly with recursion — define the base case and recursive case.
- A balanced tree has O(log n) height; an unbalanced tree degrades to O(n) for search operations.
Contact Us
Have a question or feedback? Fill out the form below or reach us directly at support@nvaitraining.com