Spaces:
Sleeping
Sleeping
| TREES AND BINARY TREES | |
| ====================== | |
| A tree is a hierarchical data structure. Root is the topmost node. | |
| Each node has at most one parent and zero or more children. | |
| Leaf nodes have no children. | |
| --- Binary Tree Node --- | |
| class TreeNode: | |
| def __init__(self, val=0, left=None, right=None): | |
| self.val = val | |
| self.left = left | |
| self.right = right | |
| # Build a sample tree: | |
| # 1 | |
| # / \ | |
| # 2 3 | |
| # / \ \ | |
| # 4 5 6 | |
| root = TreeNode(1) | |
| root.left = TreeNode(2) | |
| root.right = TreeNode(3) | |
| root.left.left = TreeNode(4) | |
| root.left.right = TreeNode(5) | |
| root.right.right = TreeNode(6) | |
| --- Tree Traversals --- | |
| # Inorder (Left, Root, Right) — gives sorted output for BST | |
| def inorder(root): | |
| if not root: | |
| return [] | |
| return inorder(root.left) + [root.val] + inorder(root.right) | |
| # Preorder (Root, Left, Right) — used to copy the tree | |
| def preorder(root): | |
| if not root: | |
| return [] | |
| return [root.val] + preorder(root.left) + preorder(root.right) | |
| # Postorder (Left, Right, Root) — used to delete the tree | |
| def postorder(root): | |
| if not root: | |
| return [] | |
| return postorder(root.left) + postorder(root.right) + [root.val] | |
| # Level Order (BFS) | |
| from collections import deque | |
| def level_order(root): | |
| if not root: | |
| return [] | |
| result, queue = [], deque([root]) | |
| while queue: | |
| node = queue.popleft() | |
| result.append(node.val) | |
| if node.left: queue.append(node.left) | |
| if node.right: queue.append(node.right) | |
| return result | |
| print(inorder(root)) # [4, 2, 5, 1, 3, 6] | |
| print(preorder(root)) # [1, 2, 4, 5, 3, 6] | |
| print(postorder(root)) # [4, 5, 2, 6, 3, 1] | |
| print(level_order(root)) # [1, 2, 3, 4, 5, 6] | |
| --- Tree Height --- | |
| def height(root): | |
| if not root: | |
| return 0 | |
| return 1 + max(height(root.left), height(root.right)) | |
| print(height(root)) # Output: 3 | |
| --- Count Nodes --- | |
| def count_nodes(root): | |
| if not root: | |
| return 0 | |
| return 1 + count_nodes(root.left) + count_nodes(root.right) | |
| --- Binary Search Tree (BST) --- | |
| Left child < Parent < Right child. | |
| Insert, Search, Delete: O(log n) average, O(n) worst (skewed). | |
| # Insert into BST | |
| def bst_insert(root, val): | |
| if not root: | |
| return TreeNode(val) | |
| if val < root.val: | |
| root.left = bst_insert(root.left, val) | |
| else: | |
| root.right = bst_insert(root.right, val) | |
| return root | |
| # Search in BST | |
| def bst_search(root, val): | |
| if not root or root.val == val: | |
| return root | |
| if val < root.val: | |
| return bst_search(root.left, val) | |
| return bst_search(root.right, val) | |
| # Delete from BST | |
| def bst_delete(root, val): | |
| if not root: | |
| 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: | |
| if not root.left: | |
| return root.right | |
| if not root.right: | |
| return root.left | |
| # Find inorder successor (smallest in right subtree) | |
| curr = root.right | |
| while curr.left: | |
| curr = curr.left | |
| root.val = curr.val | |
| root.right = bst_delete(root.right, curr.val) | |
| return root | |
| bst = None | |
| for v in [5, 3, 7, 1, 4, 6, 8]: | |
| bst = bst_insert(bst, v) | |
| print(inorder(bst)) # [1, 3, 4, 5, 6, 7, 8] | |
| --- Check if Balanced --- | |
| A tree is balanced if height difference of left and right subtrees is at most 1 for every node. | |
| def is_balanced(root): | |
| def check(node): | |
| if not node: | |
| return 0 | |
| left = check(node.left) | |
| right = check(node.right) | |
| if left == -1 or right == -1 or abs(left - right) > 1: | |
| return -1 | |
| return 1 + max(left, right) | |
| return check(root) != -1 | |
| --- Lowest Common Ancestor (LCA) --- | |
| Find the lowest node that has both p and q as descendants. | |
| def lca(root, p, q): | |
| if not root or root == p or root == q: | |
| return root | |
| left = lca(root.left, p, q) | |
| right = lca(root.right, p, q) | |
| if left and right: | |
| return root # p and q on different sides | |
| return left or right | |
| --- Level Order by Levels --- | |
| Return nodes grouped by level. | |
| def level_order_levels(root): | |
| if not root: | |
| return [] | |
| result, queue = [], deque([root]) | |
| while queue: | |
| level = [] | |
| for _ in range(len(queue)): | |
| 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 | |
| print(level_order_levels(root)) | |
| # Output: [[1], [2, 3], [4, 5, 6]] | |
| --- Diameter of Binary Tree --- | |
| Longest path between any two nodes (path may not pass through root). | |
| def diameter(root): | |
| max_d = [0] | |
| def depth(node): | |
| if not node: | |
| return 0 | |
| left = depth(node.left) | |
| right = depth(node.right) | |
| max_d[0] = max(max_d[0], left + right) | |
| return 1 + max(left, right) | |
| depth(root) | |
| return max_d[0] | |
| --- Invert Binary Tree --- | |
| Mirror the tree — swap left and right children of every node. | |
| def invert_tree(root): | |
| if not root: | |
| return None | |
| root.left, root.right = invert_tree(root.right), invert_tree(root.left) | |
| return root | |
| --- AVL Tree (Self-Balancing BST) --- | |
| Keeps height O(log n) by rotating after every insert/delete. | |
| Balance factor = height(left) - height(right). Must be -1, 0, or 1. | |
| def get_height(node): | |
| return node.height if node else 0 | |
| def get_balance(node): | |
| return get_height(node.left) - get_height(node.right) if node else 0 | |
| # Right rotation | |
| def rotate_right(y): | |
| x = y.left | |
| T2 = x.right | |
| x.right = y | |
| y.left = T2 | |
| y.height = 1 + max(get_height(y.left), get_height(y.right)) | |
| x.height = 1 + max(get_height(x.left), get_height(x.right)) | |
| return x | |
| # Left rotation | |
| def rotate_left(x): | |
| y = x.right | |
| T2 = y.left | |
| y.left = x | |
| x.right = T2 | |
| x.height = 1 + max(get_height(x.left), get_height(x.right)) | |
| y.height = 1 + max(get_height(y.left), get_height(y.right)) | |
| return y | |