class Graph:
def __init__(self) -> None:
self.graph = {}
def add_vertex(self, vertex: str) -> None:
if vertex not in self.graph:
self.graph[vertex] = []
def add_edge(self, a: str, b: str) -> None:
self.add_vertex(a)
self.add_vertex(b)
self.graph[a].append(b)
self.graph[b].append(a)
def get_neighbors(self, vertex: str) -> list[str]:
return self.graph.get(vertex, [])
def __repr__(self) -> str:
output = ''
for vertex, neighbors in self.graph.items():
output += f'{vertex} - {' - '.join(neighbors)}\n'
return outputfrom typing import Any
class TreeNode:
def __init__(self, data: Any) -> None:
self.data = data
self.left = None
self.right = None
class BinarySearchTree:
def __init__(self) -> None:
self.root = None
def insert(self, data: Any) -> None:
if not self.root:
self.root = TreeNode(data)
else:
self.insert_node(self.root, data)
def insert_node(self, node: TreeNode | None, data: Any) -> None:
if data < node.data:
if not node.left:
node.left = TreeNode(data)
else:
self.insert_node(node.left, data)
else:
if not node.right:
node.right = TreeNode(data)
else:
self.insert_node(node.right, data)
def __repr__(self) -> str:
return 'Empty tree' if not self.root else self._print_tree(self.root, '', True)
def _print_tree(self, node: TreeNode | None, prefix: str, is_left: bool) -> str:
if node is None:
return ''
result = ''
result += self._print_tree(node.right, prefix + ('│ ' if is_left else ' '), False)
result += prefix + ('└── ' if is_left else '┌── ') + str(node.data) + '\n'
result += self._print_tree(node.left, prefix + (' ' if is_left else '│ '), True)
return resultclass TrieNode:
def __init__(self) -> None:
self.children = {}
self.is_word = False
class Trie:
def __init__(self) -> None:
self.root = TrieNode()
def build(self, words: list[str]) -> None:
for word in words:
self.insert(word)
def insert(self, word: str) -> None:
node = self.root
for char in word:
if char not in node.children:
node.children[char] = TrieNode()
node = node.children[char]
node.is_word = True
def search(self, word: str) -> bool:
node = self.root
for char in word:
if char not in node.children:
return False
node = node.children[char]
return node.is_word
def starts_with(self, prefix: str) -> bool:
node = self.root
for char in prefix:
if char not in node.children:
return False
node = node.children[char]
return True
def collect_words(self, node: TrieNode, prefix: str) -> list[str]:
words = []
if node.is_word:
words.append(prefix)
for char, child_node in node.children.items():
words.extend(self.collect_words(child_node, prefix + char))
return words
def __repr__(self) -> str:
return 'Trie:\n' + self._print_trie(self.root)
def _print_trie(self, node: TrieNode | None, level: int = 0, prefix: str = '') -> str:
output = ''
prefix_str = ' ' * level + prefix
if not node:
return output
if node.is_word:
output += prefix_str + ' ├─ ' + '(*)' + '\n'
for i, (char, child_node) in enumerate(node.children.items()):
is_last = i == len(node.children) - 1
marker = '└─ ' if is_last else '├─ '
output += prefix_str + marker + char + '\n'
output += self._print_trie(child_node, level + 1, ' │' if not is_last else ' ')
return outputclass UnionFind:
def __init__(self, n: int) -> None:
self.root = list(range(n))
def find(self, a: int) -> int:
return a if a == self.root[a] else self.find(self.root[a])
def union(self, a: int, b: int) -> None:
self.root[self.find(a)] = self.find(b)
def connected(self, a: int, b: int) -> bool:
return self.find(a) == self.find(b)
def __repr__(self) -> str:
n = len(self.root)
lines = []
components = {}
for i in range(n):
root = self.find(i)
if root not in components:
components[root] = []
components[root].append(i)
for component in components.values():
lines.append(' - '.join(f'({node})' for node in component))
return '\n'.join(lines)class UnionFind:
def __init__(self, n: int) -> None:
self.root = list(range(n))
self.rank = [1] * n
def find(self, a: int) -> int:
return a if a == self.root[a] else self.find(self.root[a])
def union(self, a: int, b: int) -> None:
root_a = self.find(a)
root_b = self.find(b)
if root_a != root_b:
if self.rank[root_a] < self.rank[root_b]:
self.root[root_a] = root_b
elif self.rank[root_a] > self.rank[root_b]:
self.root[root_b] = root_a
else:
self.root[root_b] = root_a
self.rank[root_a] += 1
def connected(self, a: int, b: int) -> bool:
return self.find(a) == self.find(b)
def __repr__(self) -> str:
n = len(self.root)
lines = []
components = {}
for i in range(n):
root = self.find(i)
if root not in components:
components[root] = []
components[root].append(i)
for component in components.values():
lines.append(' - '.join(f'({node})' for node in component))
return '\n'.join(lines)def is_power_of_two(num: int) -> bool:
return (num & (num - 1)) == 0def clear_kth_bit(num: int, k: int) -> int:
return num & ~(1 << k)def count_set_bits(num: int) -> int:
return bin(num).count('1')def divide_by_power_of_two(num: int, k: int) -> int:
return num >> kdef get_rightmost_set_bit(num: int) -> int:
return num & -numdef multiply_by_power_of_two(num: int, k: int) -> int:
return num << kdef bellman_ford(n: int, edges: list[tuple[int, int, int]], source: int) -> list[int]:
distances = [float('inf')] * n
distances[source] = 0
for _ in range(n - 1):
for u, v, w in edges:
if distances[u] != float('inf') and distances[u] + w < distances[v]:
distances[v] = distances[u] + w
for u, v, w in edges:
if distances[u] != float('inf') and distances[u] + w < distances[v]:
return []
return distancesfrom collections import deque
def fn(graph):
que = deque([START_NODE])
seen = {START_NODE}
ans = 0
while que:
node = que.popleft()
# TODO: logic
for neighbor in graph[node]:
if neighbor not in seen:
seen.add(neighbor)
que.append(neighbor)
return ansdef fn(graph):
stack = [START_NODE]
seen = {START_NODE}
ans = 0
while stack:
node = stack.pop()
# TODO: logic
for neighbor in graph[node]:
if neighbor not in seen:
seen.add(neighbor)
stack.append(neighbor)
return ansdef fn(graph):
def dfs(node):
ans = 0
# TODO: logic
for neighbor in graph[node]:
if neighbor not in seen:
seen.add(neighbor)
ans += dfs(neighbor)
return ans
seen = {START_NODE}
return dfs(START_NODE)from heapq import heappop, heappush
def dijkstras(graph: list[list[tuple[int, int]]], source: int) -> list[int]:
n = len(graph)
distances = [float('inf')] * n
distances[source] = 0
heap = [(0, source)]
while heap:
curr_dist, node = heappop(heap)
if curr_dist > distances[node]:
continue
for neighbor, weight in graph[node]:
dist = curr_dist + weight
if dist < distances[neighbor]:
distances[neighbor] = dist
heappush(heap, (dist, neighbor))
return distancesfrom collections import defaultdict, deque
def kahn_topological_sort(graph: dict[int, list[int]]) -> list[int]:
result = []
indegree = defaultdict(int)
for vertices in graph.values():
for v in vertices:
indegree[v] += 1
que = deque([node for node in graph if not indegree[node]])
while que:
node = que.popleft()
result.append(node)
for neighbor in graph[node]:
indegree[neighbor] -= 1
if not indegree[neighbor]:
que.append(neighbor)
return result if len(result) == len(graph) else []def kruskal_mst(n: int, edges: list[tuple[int, int, int]]) -> list[tuple[int, int, int]]:
mst = []
uf = UnionFind(n)
edges.sort()
for w, u, v in edges:
if not uf.connected(u, v):
uf.union(u, v)
mst.append((w, u, v))
return mstfrom heapq import heappop
def prim_mst(n: int, edges: list[tuple[int, int, int]]) -> list[tuple[int, int, int]]:
mst = []
uf = UnionFind(n)
edges.sort()
while edges:
w, u, v = heappop(edges)
if not uf.connected(u, v):
uf.union(u, v)
mst.append((w, u, v))
return mstdef topological_sort(digraph):
# digraph is a dictionary:
# key: a node
# value: a set of adjacent neighboring nodes
# construct a dictionary mapping nodes to their
# indegrees
indegrees = {node: 0 for node in digraph}
for node in digraph:
for neighbor in digraph[node]:
indegrees[neighbor] += 1
# track nodes with no incoming edges
nodes_with_no_incoming_edges = []
for node in digraph:
if indegrees[node] == 0:
nodes_with_no_incoming_edges.append(node)
# initially, no nodes in our ordering
topological_ordering = []
# as long as there are nodes with no incoming edges
# that can be added to the ordering
while len(nodes_with_no_incoming_edges) > 0:
# add one of those nodes to the ordering
node = nodes_with_no_incoming_edges.pop()
topological_ordering.append(node)
# decrement the indegree of that node's neighbors
for neighbor in digraph[node]:
indegrees[neighbor] -= 1
if indegrees[neighbor] == 0:
nodes_with_no_incoming_edges.append(neighbor)
# we've run out of nodes with no incoming edges
# did we add all the nodes or find a cycle?
if len(topological_ordering) == len(digraph):
return topological_ordering # got them all
else:
raise Exception("Graph has a cycle! No topological ordering exists.")import random
def bogo_sort(arr: list) -> None:
target = sorted(arr)
while arr != target:
random.shuffle(arr)def bucket_sort(arr: list) -> list:
num_buckets = 10
min_num = min(arr)
max_num = max(arr)
bucket_size = (max_num - min_num) / num_buckets
buckets = [[] for _ in range(num_buckets)]
for num in arr:
index = min(int((num - min_num) / bucket_size), num_buckets - 1)
buckets[index].append(num)
return [num for bucket in buckets for num in sorted(bucket)]def counting_sort(arr: list) -> list:
max_num = max(arr)
min_num = min(arr)
count_range = max_num - min_num + 1
count = [0] * count_range
output = [0] * len(arr)
for num in arr:
count[num - min_num] += 1
for i in range(1, count_range):
count[i] += count[i - 1]
for num in arr[::-1]:
output[count[num - min_num] - 1] = num
count[num - min_num] -= 1
return outputdef cube_sort(arr: list, processors: int) -> None:
n = len(arr)
subarrays = []
subarray_size = n // processors
for i in range(processors):
subarray = arr[i * subarray_size : (i + 1) * subarray_size]
subarrays.append(subarray)
for subarray in subarrays:
subarray.sort()
for dimension in range(processors.bit_length() - 1):
for i in range(processors):
partner = i ^ (1 << dimension)
if i < partner:
merged = subarrays[i] + subarrays[partner]
else:
merged = subarrays[partner] + subarrays[i]
merged.sort()
subarrays[i] = merged[:subarray_size]
subarrays[partner] = merged[subarray_size:]
arr[:] = [num for subarray in subarrays for num in subarray]def pancake_sort(arr: list) -> None:
n = len(arr)
for size in reversed(range(2, n + 1)):
max_idx = find_max_index(arr, size)
if max_idx != size - 1:
flip(arr, max_idx)
flip(arr, size - 1)
def flip(arr: list, i: int) -> None:
left = 0
while left < i:
arr[left], arr[i] = arr[i], arr[left]
left += 1
i -= 1
def find_max_index(arr: list, n: int) -> int:
max_idx = 0
for i in range(n):
if arr[i] > arr[max_idx]:
max_idx = i
return max_idxdef radix_sort(arr: list) -> None:
max_val = max(arr)
exp = 1
while max_val // exp > 0:
counting_sort(arr, exp)
exp *= 10
def counting_sort(arr: list, exp: int) -> None:
n = len(arr)
output = [0] * n
count = [0] * 10
for i in range(n):
idx = arr[i] // exp
count[idx % 10] += 1
for i in range(1, 10):
count[i] += count[i - 1]
i = n - 1
while i >= 0:
idx = arr[i] // exp
output[count[idx % 10] - 1] = arr[i]
count[idx % 10] -= 1
i -= 1
for i in range(n):
arr[i] = output[i]def shell_sort(arr: list) -> None:
n = len(arr)
gaps = [701, 301, 132, 57, 23, 10, 4, 1]
for gap in gaps:
for i in range(gap, n):
tmp = arr[i]
j = i
while j >= gap and arr[j - gap] > tmp:
arr[j] = arr[j - gap]
j -= gap
if j != i:
arr[j] = tmpfrom threading import Thread
from time import sleep
def sleep_sort(arr: list ) -> list:
sorted_arr = []
threads = []
for num in arr:
thread = Thread(target=snorlax, args=(num, sorted_arr))
threads.append(thread)
for thread in threads:
thread.start( )
for thread in threads:
thread.join()
return sorted_arr
def snorlax(num: float, arr: list) -> None:
sleep(num / 1000.0)
arr.append(num)def tim_sort(arr: list) -> list:
n = len(arr)
min_run = 32
for start in range(0, n, min_run):
end = min(start + min_run - 1, n - 1)
insertion_sort(arr, start, end)
size = min_run
while size < n:
for left in range(0, n, 2 * size):
mid = min(n - 1, left + size - 1)
right = min((left + 2 * size - 1), (n - 1))
arr[left : right + 1] = merge(arr[left : mid + 1], arr[mid + 1 : right + 1])
size *= 2
return arr
def insertion_sort(arr: list, left: int, right: int) -> None:
for i in range(left + 1, right + 1):
key = arr[i]
while i > 0 and key < arr[i - 1]:
arr[i] = arr[i - 1]
i -= 1
arr[i] = key
def merge_sort(arr: list) -> list:
n = len(arr)
if n <= 1:
return arr
mid = n // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left: list, right: list) -> list:
output = []
while left and right:
min_num = left.pop(0) if left[0] <= right[0] else right.pop(0)
output.append(min_num)
output.extend(left)
output.extend(right)
return output