CodeSage / data /docs /trees.txt
Aditya
Add LLM vs RAG vs Fine-Tuning comparison project
4a3f117
Raw
History Blame Contribute Delete
6.05 kB
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