跳转到内容

Trees and Graphs

此内容尚不支持你的语言。

Connections and hierarchies: Trees are hierarchies (file systems, organisational charts), graphs are networks (roads, social connections). Trees are special graphs, connected, acyclic, with a clear parent-child relationship. Understanding both is essential for modelling real-world relationships.

Why it matters: Trees and graphs appear everywhere, DOM trees in web browsers, dependency graphs in package managers, social networks, and GPS navigation. Knowing the right traversal and algorithm for each structure is fundamental.

The key insight: DFS uses a stack (or recursion) and explores depth-first, good for detecting cycles and topological sorting. BFS uses a queue and explores breadth-first, good for shortest paths in unweighted graphs.

A tree is a connected, acyclic, undirected graph. In computer science, trees are rooted and Directed (parent to child).

TermDefinition
RootThe topmost node, with no parent
LeafA node with no children
DepthDistance from the root to a node (root has depth 0)
HeightDistance from a node to its deepest descendant (leaf has height 0)
LevelAll nodes at the same depth
SubtreeA node and all its descendants
DegreeNumber of children of a node
PathSequence of nodes from one node to another

A binary tree is a tree where each node has at most two children: left and right.

TypePropertyNodes in tree of height hh
FullEvery node has 0 or 2 children2h+12h + 1 (odd)
CompleteAll levels filled except possibly the last, which is filled left to right2h2^h to 2h+1−12^{h+1} - 1
PerfectAll internal nodes have 2 children, all leaves at same depth2h+1−12^{h+1} - 1
BalancedHeight is O(log⁡n)O(\log n)Varies
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
graph TD
    R[1] --> L[2]
    R --> RI[3]
    L --> LL[4]
    L --> LR[5]
    RI --> RL[6]
    RI --> RR[7]

    style R fill:#e74c3c,color:#fff
    style L fill:#e67e22,color:#fff
    style RI fill:#e67e22,color:#fff
    style LL fill:#3498db,color:#fff
    style LR fill:#3498db,color:#fff
    style RL fill:#3498db,color:#fff
    style RR fill:#3498db,color:#fff

Inorder (Left, Root, Right): 4, 2, 5, 1, 6, 3, 7

def inorder(root):
"""Left -> Root -> Right. Yields sorted order for BST."""
if root is None:
return
inorder(root.left)
print(root.val, end=" ')
inorder(root.right)

Preorder (Root, Left, Right): 1, 2, 4, 5, 3, 6, 7

def preorder(root):
"""Root -> Left -> Right. Used for tree serialisation."""
if root is None:
return
print(root.val, end=' ')
preorder(root.left)
preorder(root.right)

Postorder (Left, Right, Root): 4, 5, 2, 6, 7, 3, 1

def postorder(root):
"""Left -> Right -> Root. Used for tree deletion, expression evaluation."""
if root is None:
return
postorder(root.left)
postorder(root.right)
print(root.val, end=' ')

Level-order (BFS): 1, 2, 3, 4, 5, 6, 7

from collections import deque
def level_order(root):
"""BFS traversal by level."""
if root is None:
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
def inorder_iterative(root):
"""Iterative inorder using explicit stack. O(n) time, O(h) space."""
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
def preorder_iterative(root):
"""Iterative preorder. O(n) time, O(h) space."""
if not root:
return []
result = []
stack = [root]
while stack:
node = stack.pop()
result.append(node.val)
# Push right first so left is processed first
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return result
def max_depth(root):
"""Maximum depth of a binary tree. O(n) time, O(h) space."""
if root is None:
return 0
return 1 + max(max_depth(root.left), max_depth(root.right))
def is_balanced(root):
"""
Check if a binary tree is height-balanced.
A tree is balanced if the heights of the two subtrees of every node differ by at most 1.
Time: O(n), Space: O(h)
"""
def check(node):
if node is None:
return 0, True
left_height, left_balanced = check(node.left)
right_height, right_balanced = check(node.right)
height = 1 + max(left_height, right_height)
balanced = (left_balanced and right_balanced and
abs(left_height - right_height) <= 1)
return height, balanced
_, balanced = check(root)
return balanced
def is_same_tree(p, q):
"""Check if two binary trees are identical. O(n) time."""
if p is None and q is None:
return True
if p is None or q is None:
return False
return (p.val == q.val and
is_same_tree(p.left, q.left) and
is_same_tree(p.right, q.right))

A BST is a binary tree where for every node: all values in the left subtree are less than the node’s Value, and all values in the right subtree are greater.

def bst_search(root, val):
"""Search for a value in a BST. O(h) time where h = height."""
current = root
while current:
if val == current.val:
return current
elif val < current.val:
current = current.left
else:
current = current.right
return None
def bst_insert(root, val):
"""Insert a value into a BST. O(h) time."""
if root is None:
return TreeNode(val)
if val < root.val:
root.left = bst_insert(root.left, val)
elif val > root.val:
root.right = bst_insert(root.right, val)
return root
def bst_delete(root, val):
"""
Delete a value from a BST.
O(h) time. Three cases:
1. Leaf: just remove
2. One child: replace with child
3. Two children: replace with in-order successor, delete successor
"""
if root is None:
return None
if val < root.val:
root.left = bst_delete(root.left, val)
elif val > root.val:
root.right = bst_delete(root.right, val)
else:
# Found the node to delete
if root.left is None:
return root.right
if root.right is None:
return root.left
# Two children: find in-order successor (smallest in right subtree)
successor = root.right
while successor.left:
successor = successor.left
root.val = successor.val
root.right = bst_delete(root.right, successor.val)
return root
OperationAverage (balanced)Worst (degenerate)
SearchO(log⁡n)O(\log n)O(n)O(n)
InsertO(log⁡n)O(\log n)O(n)O(n)
DeleteO(log⁡n)O(\log n)O(n)O(n)
Min/MaxO(log⁡n)O(\log n)O(n)O(n)
In-order traversalO(n)O(n)O(n)O(n)
def is_valid_bst(root):
"""
Check if a binary tree is a valid BST.
Time: O(n), Space: O(h)
"""
def validate(node, min_val, max_val):
if node is None:
return True
if node.val <= min_val or node.val >= max_val:
return False
return (validate(node.left, min_val, node.val) and
validate(node.right, node.val, max_val))
return validate(root, float('-inf'), float('inf'))