Trees and Graphs
此内容尚不支持你的语言。
Intuition
Section titled “Intuition”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.
Tree Terminology
Section titled “Tree Terminology”A tree is a connected, acyclic, undirected graph. In computer science, trees are rooted and Directed (parent to child).
| Term | Definition |
|---|---|
| Root | The topmost node, with no parent |
| Leaf | A node with no children |
| Depth | Distance from the root to a node (root has depth 0) |
| Height | Distance from a node to its deepest descendant (leaf has height 0) |
| Level | All nodes at the same depth |
| Subtree | A node and all its descendants |
| Degree | Number of children of a node |
| Path | Sequence of nodes from one node to another |
Binary Trees
Section titled “Binary Trees”A binary tree is a tree where each node has at most two children: left and right.
Classifications
Section titled “Classifications”| Type | Property | Nodes in tree of height |
|---|---|---|
| Full | Every node has 0 or 2 children | (odd) |
| Complete | All levels filled except possibly the last, which is filled left to right | to |
| Perfect | All internal nodes have 2 children, all leaves at same depth | |
| Balanced | Height is | Varies |
Node Definition
Section titled “Node Definition”class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = rightTraversal
Section titled “Traversal”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:#fffInorder (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 resultIterative Traversals
Section titled “Iterative Traversals”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 resultTree Properties
Section titled “Tree Properties”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))Binary Search Trees (BST)
Section titled “Binary Search Trees (BST)”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.
Operations
Section titled “Operations”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 rootBST Complexity
Section titled “BST Complexity”| Operation | Average (balanced) | Worst (degenerate) |
|---|---|---|
| Search | ||
| Insert | ||
| Delete | ||
| Min/Max | ||
| In-order traversal |
Validate BST
Section titled “Validate BST”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'))