# Quick Sort Implementation
def quicksort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + middle + quicksort(right)
# Merge Sort Implementation
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left, right):
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
Searching Algorithms
Searching
Algorithm
Time
Space
Linear Search
O(n)
O(1)
Binary Search
O(log n)
O(1)
Interpolation Search
O(log log n)
O(1)
Exponential Search
O(log n)
O(1)
# Binary Search Implementation
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
# Linear Search Implementation
def linear_search(arr, target):
for i, val in enumerate(arr):
if val == target:
return i
return -1
# Binary Search (Recursive)
def binary_search_recursive(arr, target, left, right):
if left > right:
return -1
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
return binary_search_recursive(arr, target, mid + 1, right)
else:
return binary_search_recursive(arr, target, left, mid - 1)
Recursion & Backtracking
Recursion
Problem
Time
Factorial
O(n)
Fibonacci
O(2ⁿ)
N-Queens
O(n!)
Subset Sum
O(2ⁿ)
# Factorial Recursion
def factorial(n):
if n <= 1:
return 1
return n * factorial(n - 1)
# Fibonacci Recursion
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# N-Queens Backtracking
def solve_n_queens(n):
def is_safe(board, row, col):
for i in range(row):
if board[i] == col or abs(board[i] - col) == abs(i - row):
return False
return True
def backtrack(board, row):
if row == n:
return [board[:]]
solutions = []
for col in range(n):
if is_safe(board, row, col):
board[row] = col
solutions.extend(backtrack(board, row + 1))
return solutions
return backtrack([0] * n, 0)
Divide & Conquer
Divide & Conquer
Algorithm
Time
Merge Sort
O(n log n)
Quick Sort
O(n log n)
Binary Search
O(log n)
Strassen Matrix
O(n^2.807)
# Karatsuba Multiplication
def karatsuba(x, y):
if x < 10 or y < 10:
return x * y
n = max(len(str(x)), len(str(y)))
m = n // 2
a = x // 10**m
b = x % 10**m
c = y // 10**m
d = y % 10**m
ac = karatsuba(a, c)
bd = karatsuba(b, d)
ad_bc = karatsuba(a + b, c + d) - ac - bd
return ac * 10**(2*m) + ad_bc * 10**m + bd
# Closest Pair of Points
def closest_pair(points):
if len(points) <= 3:
return min_distance_brute_force(points)
mid = len(points) // 2
left = points[:mid]
right = points[mid:]
d1 = closest_pair(left)
d2 = closest_pair(right)
d = min(d1, d2)
# Check strip
strip = [p for p in points if abs(p[0] - points[mid][0]) < d]
return min(d, strip_closest(strip, d))
Greedy Algorithms
Greedy
Problem
Time
Activity Selection
O(n log n)
Huffman Coding
O(n log n)
Fractional Knapsack
O(n log n)
Dijkstra's
O((V+E) log V)
# Activity Selection Problem
def activity_selection(start, finish):
n = len(start)
activities = []
i = 0
activities.append(i)
for j in range(1, n):
if start[j] >= finish[i]:
activities.append(j)
i = j
return activities
# Fractional Knapsack
def fractional_knapsack(values, weights, capacity):
items = [(values[i], weights[i], values[i]/weights[i]) for i in range(len(values))]
items.sort(key=lambda x: x[2], reverse=True)
total_value = 0
for value, weight, ratio in items:
if capacity >= weight:
total_value += value
capacity -= weight
else:
total_value += ratio * capacity
break
return total_value
# Huffman Coding
import heapq
def huffman_coding(freq):
heap = [[f, [char, ""]] for char, f in freq.items()]
heapq.heapify(heap)
while len(heap) > 1:
lo = heapq.heappop(heap)
hi = heapq.heappop(heap)
for pair in lo[1:]:
pair[1] = '0' + pair[1]
for pair in hi[1:]:
pair[1] = '1' + pair[1]
heapq.heappush(heap, [lo[0] + hi[0]] + lo[1:] + hi[1:])
return heap[0][1:]
Dynamic Programming
DP
Problem
Time
Space
Fibonacci
O(n)
O(1)
LCS
O(mn)
O(mn)
Edit Distance
O(mn)
O(mn)
0/1 Knapsack
O(nW)
O(nW)
# Fibonacci with DP
def fibonacci_dp(n):
if n <= 1:
return n
dp = [0] * (n + 1)
dp[1] = 1
for i in range(2, n + 1):
dp[i] = dp[i-1] + dp[i-2]
return dp[n]
# Longest Common Subsequence
def lcs(text1, text2):
m, n = len(text1), len(text2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
return dp[m][n]
# 0/1 Knapsack
def knapsack_01(values, weights, capacity):
n = len(values)
dp = [[0] * (capacity + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for w in range(capacity + 1):
if weights[i-1] <= w:
dp[i][w] = max(dp[i-1][w],
dp[i-1][w - weights[i-1]] + values[i-1])
else:
dp[i][w] = dp[i-1][w]
return dp[n][capacity]
Graph Algorithms
Graph
Algorithm
Time
BFS
O(V + E)
DFS
O(V + E)
Dijkstra's
O((V+E) log V)
Bellman-Ford
O(VE)
Floyd-Warshall
O(V³)
Kruskal's MST
O(E log E)
Prim's MST
O(E log V)
# BFS Implementation
from collections import deque
def bfs(graph, start):
visited = set()
queue = deque([start])
visited.add(start)
while queue:
vertex = queue.popleft()
print(vertex, end=' ')
for neighbor in graph[vertex]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
# DFS Implementation
def dfs(graph, start, visited=None):
if visited is None:
visited = set()
visited.add(start)
print(start, end=' ')
for neighbor in graph[start]:
if neighbor not in visited:
dfs(graph, neighbor, visited)
# Dijkstra's Algorithm
import heapq
def dijkstra(graph, start):
distances = {vertex: float('infinity') for vertex in graph}
distances[start] = 0
pq = [(0, start)]
while pq:
current_distance, current_vertex = heapq.heappop(pq)
if current_distance > distances[current_vertex]:
continue
for neighbor, weight in graph[current_vertex].items():
distance = current_distance + weight
if distance < distances[neighbor]:
distances[neighbor] = distance
heapq.heappush(pq, (distance, neighbor))
return distances
# Topological Sort
def topological_sort(graph):
def dfs(node):
visited.add(node)
visiting.add(node)
for neighbor in graph.get(node, []):
if neighbor in visiting:
return False # Cycle detected
if neighbor not in visited:
if not dfs(neighbor):
return False
visiting.remove(node)
result.append(node)
return True
visited = set()
visiting = set()
result = []
for node in graph:
if node not in visited:
if not dfs(node):
return [] # Cycle detected
return result[::-1]
Tree Algorithms
Tree
Traversal
Order
Inorder
Left → Root → Right
Preorder
Root → Left → Right
Postorder
Left → Right → Root
Level Order
Level by Level
# Tree Node Definition
class TreeNode:
def __init__(self, val=0):
self.val = val
self.left = None
self.right = None
# Inorder Traversal
def inorder_traversal(root):
if root:
inorder_traversal(root.left)
print(root.val, end=' ')
inorder_traversal(root.right)
# Preorder Traversal
def preorder_traversal(root):
if root:
print(root.val, end=' ')
preorder_traversal(root.left)
preorder_traversal(root.right)
# Postorder Traversal
def postorder_traversal(root):
if root:
postorder_traversal(root.left)
postorder_traversal(root.right)
print(root.val, end=' ')
# Level Order Traversal (BFS)
from collections import deque
def level_order_traversal(root):
if not root:
return []
queue = deque([root])
result = []
while queue:
level = []
level_size = len(queue)
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
# BST Insert
def insert_bst(root, val):
if not root:
return TreeNode(val)
if val < root.val:
root.left = insert_bst(root.left, val)
else:
root.right = insert_bst(root.right, val)
return root
String Algorithms
String
Algorithm
Time
KMP
O(m + n)
Rabin-Karp
O(m + n)
Boyer-Moore
O(m + n)
Longest Palindromic
O(n²)
# KMP Algorithm
def kmp_search(text, pattern):
def compute_lps(pattern):
lps = [0] * len(pattern)
length = 0
i = 1
while i < len(pattern):
if pattern[i] == pattern[length]:
length += 1
lps[i] = length
i += 1
else:
if length != 0:
length = lps[length - 1]
else:
lps[i] = 0
i += 1
return lps
lps = compute_lps(pattern)
i = j = 0
matches = []
while i < len(text):
if pattern[j] == text[i]:
i += 1
j += 1
if j == len(pattern):
matches.append(i - j)
j = lps[j - 1]
elif i < len(text) and pattern[j] != text[i]:
if j != 0:
j = lps[j - 1]
else:
i += 1
return matches
# Longest Palindromic Substring
def longest_palindrome(s):
def expand_around_center(left, right):
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return s[left + 1:right]
if len(s) < 2:
return s
longest = ""
for i in range(len(s)):
# Odd length palindrome
palindrome1 = expand_around_center(i, i)
# Even length palindrome
palindrome2 = expand_around_center(i, i + 1)
if len(palindrome1) > len(longest):
longest = palindrome1
if len(palindrome2) > len(longest):
longest = palindrome2
return longest
# Rabin-Karp Algorithm
def rabin_karp(text, pattern):
d = 256 # Number of characters in input alphabet
q = 101 # A prime number
M = len(pattern)
N = len(text)
p = 0 # Hash value for pattern
t = 0 # Hash value for text
h = 1
# Calculate h = pow(d, M-1) % q
for i in range(M - 1):
h = (h * d) % q
# Calculate hash value of pattern and first window of text
for i in range(M):
p = (d * p + ord(pattern[i])) % q
t = (d * t + ord(text[i])) % q
# Slide the pattern over text one by one
for i in range(N - M + 1):
if p == t:
# Check for characters one by one
for j in range(M):
if text[i + j] != pattern[j]:
break
else:
return i
if i < N - M:
t = (d * (t - ord(text[i]) * h) + ord(text[i + M])) % q
if t < 0:
t = t + q
return -1
Bit Manipulation
Bit
Operation
Syntax
AND
x & y
OR
x | y
XOR
x ^ y
NOT
~x
Left Shift
x << n
Right Shift
x >> n
# Common Bit Operations
def count_set_bits(n):
count = 0
while n:
count += n & 1
n >>= 1
return count
def is_power_of_two(n):
return n > 0 and (n & (n - 1)) == 0
def get_single_number(nums):
result = 0
for num in nums:
result ^= num
return result
def reverse_bits(n):
result = 0
for i in range(32):
result = (result << 1) | (n & 1)
n >>= 1
return result
def add_without_plus(a, b):
while b:
carry = a & b
a = a ^ b
b = carry << 1
return a
# Bit manipulation tricks
def power_of_2(n):
return n > 0 and (n & (n - 1)) == 0
def count_trailing_zeros(n):
count = 0
while n and (n & 1) == 0:
count += 1
n >>= 1
return count
def swap_without_temp(a, b):
a = a ^ b
b = a ^ b
a = a ^ b
return a, b
Math Algorithms
Math
Algorithm
Time
GCD
O(log min(a,b))
Sieve of Eratosthenes
O(n log log n)
Fast Exponentiation
O(log n)
Prime Factorization
O(√n)
# GCD using Euclidean Algorithm
def gcd(a, b):
while b:
a, b = b, a % b
return a
# LCM using GCD
def lcm(a, b):
return abs(a * b) // gcd(a, b)
# Sieve of Eratosthenes
def sieve_of_eratosthenes(n):
is_prime = [True] * (n + 1)
is_prime[0] = is_prime[1] = False
for i in range(2, int(n**0.5) + 1):
if is_prime[i]:
for j in range(i*i, n + 1, i):
is_prime[j] = False
return [i for i in range(n + 1) if is_prime[i]]
# Fast Exponentiation
def fast_power(base, exponent, modulus=None):
result = 1
base = base % modulus if modulus else base
while exponent > 0:
if exponent % 2 == 1:
result = (result * base) % modulus if modulus else result * base
base = (base * base) % modulus if modulus else base * base
exponent //= 2
return result
# Prime Factorization
def prime_factorization(n):
factors = []
d = 2
while d * d <= n:
while n % d == 0:
factors.append(d)
n //= d
d += 1
if n > 1:
factors.append(n)
return factors
# Extended Euclidean Algorithm
def extended_gcd(a, b):
if a == 0:
return b, 0, 1
else:
gcd, x, y = extended_gcd(b % a, a)
return gcd, y - (b // a) * x, x
Complexity Analysis
Complexity
Complexity
Description
O(1)
Constant Time
O(log n)
Logarithmic
O(n)
Linear
O(n log n)
Linearithmic
O(n²)
Quadratic
O(2ⁿ)
Exponential
O(n!)
Factorial
# Common Complexity Examples
def constant_time():
return 1 # O(1)
def logarithmic_time(n):
i = 1
while i < n:
i *= 2 # O(log n)
def linear_time(n):
for i in range(n):
pass # O(n)
def linearithmic_time(n):
for i in range(n):
j = 1
while j < n:
j *= 2 # O(n log n)
def quadratic_time(n):
for i in range(n):
for j in range(n):
pass # O(n²)
def exponential_time(n):
if n <= 1:
return 1
return exponential_time(n-1) + exponential_time(n-2) # O(2ⁿ)
# Space Complexity Examples
def constant_space():
a = 1
b = 2 # O(1)
def linear_space(n):
arr = [0] * n # O(n)
def quadratic_space(n):
matrix = [[0] * n for _ in range(n)] # O(n²)