Syntax cheat sheet
Every line of syntax this roadmap teaches, gathered in one place — 352 of them, in the order you meet them rather than alphabetically. Each one also appears under the concept that explains it; this page is for when you are building something and cannot remember what the thing was called.
352 lines
Getting your computer ready Week 0
What Python actually is
python3 --version- Prints the installed version, which proves the command works at all.On Windows the command is usually python or py, not python3.
python3- Opens the interactive prompt, where each line runs the moment you press Enter.
exit()- Leaves the interactive prompt and returns you to the shell.
print("Hello")- Writes text to the terminal. The quotes are part of the syntax, not the message.
python3 hello.py- Runs a file from top to bottom, then exits.
# a note to yourself- A comment. Python ignores everything after the # on that line.
pip --version- Confirms the package installer is present, and says which Python it belongs to.
What a code editor is for
mkdir learn- Creates a folder for your work, from the terminal.
cd learn- Moves the terminal into that folder, so every later command runs there.
code .- Opens the current folder in VS Code. The dot means “here”.
ls- Lists what is in the current folder.Windows Command Prompt uses dir instead; PowerShell accepts ls.
Telling the computer what to do Weeks 1–3
Variables and types
count = 3- Binds the name count to the value 3. No type declaration needed.
name, age = "Ada", 36- Assigns several names at once, left to right.
count += 1- Shorthand for count = count + 1. Also -=, *=, /=.
type(count)- Tells you what kind of value a name currently holds.
total = None- The “nothing yet” value, for a name that exists but has no answer.Test it with `if total is None:`, never == None.
MAX_TRIES = 5- A constant by convention. Capitals tell a reader not to reassign it; Python will not stop you.
Getting information in and out
answer = input("Your name: ")- Prints the prompt, waits, and returns what was typed.
age = int(input("Age: "))- Converts the typed text into a whole number.input ALWAYS returns a string. "3" + 1 is an error; int("3") + 1 is 4.
print(f"{name} is {age}")- An f-string: anything inside the braces is evaluated and inserted.
print(f"{price:.2f}")- Formats a number to two decimal places.
print(a, b, sep=", ")- Prints several values with a separator you choose.
print("loading", end="")- Prints without starting a new line.
Operators — doing things to values
7 / 2- True division. Gives 3.5, a float, even when both sides are whole.
7 // 2- Floor division. Gives 3, discarding the remainder.
7 % 2- Remainder. Gives 1, and n % 2 == 0 is the standard even test.
2 ** 10- Power. Two to the tenth.
a == b- Compares values. Also !=, <, >, <=, >=.One = assigns, two == compares. Mixing them up is the classic first bug.
x > 0 and y > 0- Both must hold. Also or for either, not to invert.
"a" in word- Membership. Works on strings, lists, sets, and dictionary keys.
0 < x < 10- A chained comparison, meaning both at once. It reads exactly like the maths.
Conditionals — making decisions
if x > 0:- Runs the indented block below only when the test is true.
elif x == 0:- Checked only when every test above it failed. Use as many as you like.
else:- Runs when nothing above matched. At most one, and it takes no test.
if items:- Truthiness. An empty list, an empty string, 0 and None are all false.
label = "odd" if n % 2 else "even"- A conditional expression, for when a whole if-block would be four lines of noise.
if value is None:- An identity test, and the correct way to check for None.is asks “the same object?”, == asks “the same value?”. Use is only for None, True and False.
Loops — repeating work
for item in items:- Visits each element in turn. No index arithmetic needed.
for i in range(5):- Counts 0, 1, 2, 3, 4. The end is excluded.
for i in range(2, 10, 2):- Start, stop, step: 2, 4, 6, 8.
for i, item in enumerate(items):- Gives you the position and the value together.
for a, b in zip(xs, ys):- Walks two sequences side by side, stopping at the shorter one.
while not done:- Repeats while a condition holds. Use it when you cannot say in advance how many times.
break- Leaves the loop immediately.
continue- Skips to the next iteration without finishing this one.
Indentation is not decoration
if x: do_something()- Four spaces mark the body. The indentation IS the block — there are no braces.
pass- A block that does nothing, for when the syntax demands a body you have not written yet.
total = (first + second)- Brackets let one expression span lines, and indentation inside them is free.
Reusable steps, and Python's built-in containers Weeks 4–6
Functions
def greet(name):- Defines a function. The body is the indented block below.
return value- Hands a result back to the caller and stops the function.A function with no return gives back None.
def greet(name, greeting="Hi"):- A default, used when the caller omits that argument.
greet(name="Ada")- A keyword argument. Clearer than position once there are several.
def total(*numbers):- Collects any number of positional arguments into a tuple.
def config(**options):- Collects any number of keyword arguments into a dictionary.
def add(a: int, b: int) -> int:- Type hints. Python does not enforce them; they are for readers and tools.
"""One line saying what it does."""- A docstring, the first thing in the body. help(add) prints it.
Scope — where a name is visible
count = 0- At module level, outside any function. Readable from inside one.
global count- Declares that assignments in this function change the module-level name.Needed only to ASSIGN. Reading an outer name works without it.
nonlocal total- The same idea, but for the enclosing function rather than the module.
def f(items=None): items = items or []- The safe way to default a mutable argument.def f(items=[]) reuses ONE list across every call — a bug that costs an hour to find.
Lists
items = [1, 2, 3]- A list literal: ordered, changeable, duplicates allowed.
items[0]- The first element. items[-1] is the last.
items[1:3]- A slice — a new list holding elements 1 and 2. The end is excluded.
items.append(4)- Adds one element to the end.
items.extend([5, 6])- Adds every element of another sequence.
items.pop()- Removes and returns the last. items.pop(0) takes the first.pop(0) shifts every remaining element. Use collections.deque if you do it in a loop.
items.remove(2)- Removes the first element equal to 2. Raises ValueError if there is none.
items.sort()- Sorts in place and returns None. sorted(items) returns a new list instead.
items.sort(key=len, reverse=True)- Sorts by a computed value, longest first.
len(items)- How many elements. Works on strings, dictionaries and sets too.
copy = items[:]- A shallow copy.copy = items is the SAME list under a second name — changing one changes both.
Dictionaries
ages = {"ada": 36}- A dictionary literal: keys mapped to values.
ages["ada"]- Looks up a key. Raises KeyError if it is missing.
ages.get("bob", 0)- Looks up a key, returning a fallback instead of raising.
ages["bob"] = 41- Adds or overwrites. There is no separate insert.
"ada" in ages- Tests for a KEY, not a value. Average O(1).
for key, value in ages.items():- Walks pairs. .keys() and .values() give one side each.
ages.setdefault("cy", []).append(1)- Fetches the key, inserting a default first if it is absent. The one-line “group into buckets”.
del ages["bob"]- Removes a key. ages.pop("bob", None) removes it without raising.
ages.update(other)- Merges another dictionary in, overwriting on collision.
Tuples and sets
point = (3, 4)- A tuple: ordered like a list, but immutable.
x, y = point- Unpacking. Works for any sequence of the right length.
first, *rest = items- Takes one element and collects the remainder into a list.
seen = set()- An empty set.{} is an empty DICTIONARY, not a set. This trips up everyone once.
seen = {1, 2, 3}- A set literal: unordered, no duplicates, average O(1) membership.
seen.add(4)- Adds one element. Adding a duplicate does nothing at all.
a & b- Intersection. Also | union, - difference, ^ symmetric difference.
set(items)- Deduplicates any sequence.Order is not preserved. list(dict.fromkeys(items)) dedupes and keeps it.
List comprehensions
[x * 2 for x in items]- Builds a new list by transforming each element.
[x for x in items if x > 0]- Filters. The condition goes at the end.
[f(x) if x else 0 for x in items]- Chooses per element. This if/else goes BEFORE the for; a filtering if goes after.
{k: v for k, v in pairs}- A dictionary comprehension.
{word.lower() for word in words}- A set comprehension, which deduplicates as it builds.
sum(x * x for x in items)- A generator expression. It computes lazily, so nothing large is built in memory.
[c for word in words for c in word]- Flattens. The for clauses read left to right, exactly like nested loops.Past two clauses, write the loop out. Clever is not the goal.
Reading crashes, working with files, and finding bugs Weeks 7–10
Reading a stack trace
raise ValueError("n must be positive")- Signals a failure yourself, with a message the traceback will show.
import traceback- The module for handling tracebacks as data.
traceback.print_exc()- Prints the current exception and its traceback from inside an except block.
raise ValueError("bad row") from err- Chains a new error onto the original, so the traceback shows both causes.
Handling exceptions
try:- Starts a protected block. The risky work goes here.
except ValueError:- Handles one specific failure. Prefer this to a bare except.
except (KeyError, IndexError) as err:- Catches several kinds and binds the object to a name.
else:- Runs only if nothing was raised, keeping its own errors out of the handler.
finally:- Runs either way. The place to release things.
raise- Re-raises the exception you are handling, unchanged, after logging it.
class ParseError(Exception):- Your own exception type, so callers can catch exactly this failure.
with open(path) as f:- A context manager: cleanup happens even if the block raises.
Reading and writing files
with open("notes.txt") as f:- Opens for reading, and closes automatically at the end of the block.
text = f.read()- Reads the whole file into one string.
for line in f:- Streams one line at a time, so the file's size stops mattering.
open(path, "w")- Opens for writing.w TRUNCATES the file immediately. Use a to append.
f.write("text\n")- Writes a string. Unlike print, it adds no newline for you.
open(path, encoding="utf-8")- States the encoding explicitly, so the file reads the same on every machine.
import json- The standard reader and writer for structured data files.
json.load(f)- Reads JSON from a file into dictionaries and lists. json.dump(obj, f) writes it back.
from pathlib import Path- Paths as objects: Path("data") / "x.txt", .exists(), .read_text().
Debugging
print(f"{value=}")- Prints the expression AND its value — value=3. Made for exactly this.
breakpoint()- Stops execution here and drops you into the debugger. Delete it before committing.
n- In the debugger: run the next line. s steps into a call, c continues, q quits.
p variable- In the debugger: print a value. pp pretty-prints, l lists the source around you.
assert total >= 0, total- Crashes immediately if an assumption is false, showing the offending value.
import sys; sys.exit(1)- Stops the program with a failure code, for when continuing would do damage.
Reproducing a bug before fixing it
def test_empty_input_returns_zero():- A test is a function whose name starts with test_. pytest finds it for you.
assert total([]) == 0- The whole assertion vocabulary you need to start. pytest shows both sides on failure.
pytest- Runs every test it can find under the current folder.
pytest -k empty- Runs only tests whose name contains “empty” — the fast loop while fixing one bug.
pytest -x- Stops at the first failure instead of reporting all of them.
with pytest.raises(ValueError):- Asserts that the block DOES raise. A test that expects a crash.
@pytest.mark.parametrize("n,want", [(0, 1), (5, 120)])- Runs the same test body over several cases, reporting each separately.
Logging you will thank yourself for at 3am
import logging- The standard library's logging system. Prefer it to print for anything long-lived.
logging.basicConfig(level=logging.INFO)- Turns logging on and sets the threshold. Call it once, at program start.
log = logging.getLogger(__name__)- A logger named after the module, so the output says where each line came from.
log.info("loaded %s rows from %s", count, path)- Logs with the values filled in.Pass values as arguments rather than building an f-string; then the formatting is skipped entirely when the level is off.
log.warning("retrying %s", url)- Something odd but survivable. debug < info < warning < error < critical.
log.exception("could not parse %s", path)- Logs at error level AND attaches the traceback. Use it inside except.
logging.basicConfig(filename="app.log")- Sends records to a file instead of the terminal.
Bisecting a failure
git bisect start- Begins a bisect session.
git bisect bad- Marks the current commit as broken.
git bisect good a1b2c3d- Marks a commit you know was fine. Git then checks out the midpoint for you.
git bisect run pytest -k parse- Runs the whole search automatically, using the command's exit code as the answer.
git bisect reset- Ends the session and puts you back where you started.
git log --oneline -20- Finds a commit hash to use as your known-good point.
Modules — splitting code across files
import math- Makes the module available as math.sqrt(9).
from math import sqrt- Pulls one name in directly, so you write sqrt(9).
import numpy as np- Imports under a shorter name.
from .helpers import clean- A relative import, from a module beside this one in the same package.
if __name__ == "__main__":- Runs the block only when the file is executed directly, not when it is imported.Without it, importing your file runs its script code as a side effect.
python3 -m mypackage- Runs a package as a program.
Building your own kinds of thing Weeks 11–13
Classes and objects
class Dog:- Defines a new kind of thing. Capitalised by convention.
def __init__(self, name):- The constructor. It runs when you write Dog("Rex").
self.name = name- Stores a value on this particular object.
def bark(self):- A method. self is the first parameter of every one, and Python passes it for you.
rex = Dog("Rex")- Creates an instance. There is no new keyword.
isinstance(rex, Dog)- Asks whether an object is of a given type.
class Puppy(Dog):- Inherits everything from Dog, and may override parts.
super().__init__(name)- Calls the parent's version, so its setup still happens.
Why objects exist at all
from dataclasses import dataclass- Generates __init__, __repr__ and __eq__ from the fields you declare.
@dataclass class Point: x: int y: int- A whole class in four lines, with equality and printing that already work.
@property def area(self):- A computed attribute, read as shape.area with no parentheses.
@staticmethod- A function that lives on the class but needs no instance.
@classmethod def from_row(cls, row):- An alternative constructor, receiving the class instead of an instance.
Making objects printable
def __str__(self):- What print(obj) and str(obj) show. For humans.
def __repr__(self):- What the prompt and containers show. For you, debugging — aim for something unambiguous.
return f"Dog({self.name!r})"- !r inserts the repr, so strings keep their quotes.
def __eq__(self, other):- Defines what == means for your type.
def __len__(self):- Makes len(obj) work. Same idea for __iter__, __contains__ and __getitem__.
Composition — objects holding other objects
self.engine = Engine()- Composition: an object holding another object, and the default choice.
def start(self): return self.engine.start()- Delegation. Forward the call to the part that owns the job.
def __init__(self, store): self.store = store- Dependency injection: the collaborator is passed in, so a test can pass a fake.
class Engine(Protocol):- From typing. Describes what a collaborator must provide, with no inheritance.
A gentle first look at recursion
def countdown(n): if n == 0: return countdown(n - 1)- A function calling itself, with a base case that stops it.
return 1 if n <= 1 else n * factorial(n - 1)- The recursive step. Every call must move toward the base case.
import sys; sys.setrecursionlimit(10000)- Raises the default limit of about a thousand frames.Usually the wrong fix. Deep recursion in Python normally wants a loop and an explicit stack.
from functools import cache- Decorate with @cache and repeated calls with the same arguments are remembered instead of recomputed.
How to say whether code is fast Weeks 14–15
Big-O notation
for item in items:- One pass over the input: the usual O(n) shape.
for left in items: for right in items:- Compares every pair: O(n squared), which becomes expensive quickly.
items.sort()- Sorts a list in O(n log n) time in the usual case.
while low <= high: mid = (low + high) // 2 if items[mid] < target: low = mid + 1 else: high = mid - 1- Halves the remaining search space each turn: O(log n).
Recursion and the call stack
def factorial(n): if n <= 1: return 1 return n * factorial(n - 1)- The recursive shape: stop at a base case, then make one smaller call.
from functools import cache- Imports the decorator that remembers repeated recursive subproblems.
@cache def fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2)- Caches Fibonacci results so each n is calculated once.
stack = [root]- Starts an explicit stack, the loop-friendly replacement for deep recursion.
The small amount of maths you need
import math- Imports the standard library's number helpers.
n % 2 == 0- Tests whether n is even.
math.gcd(a, b)- Finds the greatest common divisor, useful for simplifying fractions and cycles.
math.lcm(a, b)- Finds the least common multiple.
pow(base, exponent, modulus)- Computes a power modulo modulus without constructing the huge full power.
Structures that lay things out in a line Weeks 16–20
Arrays and dynamic arrays
nums = [10, 20, 30]- Creates an ordered, indexable run of values.
nums[1]- Reads an item by position in O(1) time.
nums.append(40)- Adds an item at the end, the usual cheap array operation.
nums[1:3]- Takes a slice from index 1 up to, but not including, 3.
Linked lists
node.next = Node(value)- Links a new node after the current one.
new.next = head head = new- Inserts a node at the front in O(1) time.
current = head while current is not None: current = current.next- Walks every node from the head to the end.
slow = fast = head- Starts slow and fast pointers, useful for detecting a cycle or finding a middle.
Stacks
stack = []- Creates a stack using a Python list.
stack.append(value)- Pushes value onto the top.
value = stack.pop()- Pops and returns the most recently pushed value.
if stack:- Checks that a stack has something to pop.
Queues, deques and priority queues
from collections import deque- Imports Python's efficient queue type.
queue = deque([start])- Creates a queue with its first item already waiting.
queue.append(item)- Adds item at the back of the queue.
item = queue.popleft()- Takes the oldest item from the front in O(1) time.list.pop(0) shifts every remaining item. Use deque when you remove from the front in a loop.
Hash tables
seen = set()- Creates a hash set for fast membership checks.
counts = {}- Creates a dictionary, Python's hash table.
counts[value] = counts.get(value, 0) + 1- Counts occurrences without a separate missing-key branch.
from collections import defaultdict- Imports a dictionary that creates a default value for new keys.
groups = defaultdict(list)- Creates buckets that begin as empty lists.
Strings
word = "algorithm"- Creates an immutable sequence of characters.
word[::-1]- Returns the characters in reverse order.
word.lower()- Makes a lower-case copy before a case-insensitive comparison.
"".join(chars)- Builds one string from character pieces.Repeated text += piece in a large loop keeps making new strings; collect pieces and join once.
word.isalnum()- Checks that every character is a letter or number.
Proving code does what you think Weeks 21–23
Choosing what to assert
assert total([]) == 0- States the expected behaviour for an empty input.
assert find([7], 7) == 0- Covers the one-item boundary where loops often go wrong.
assert sorted(values) == [1, 2, 3]- Checks a visible result rather than how the function reached it.
with pytest.raises(ValueError): parse("bad")- Asserts that invalid input fails in the promised way.
Examples versus properties
assert reverse([1, 2, 3]) == [3, 2, 1]- A named example with one known answer.
assert reverse(reverse(items)) == items- A property: reversing twice restores any input.
assert sorted(result) == sorted(items)- Checks that a sort kept every original item.
assert all(result[i] <= result[i + 1] for i in range(len(result) - 1))- Checks the ordering property of every adjacent pair.
Keeping fixed bugs fixed
def test_empty_input_regression():- Names a test after the bug it prevents from returning.
assert parse("") == []- Locks down the smallest input that previously broke.
pytest -k regression- Runs only tests whose names include regression.
pytest -x- Stops at the first failure while you are fixing a bug.
Judging generated code
assert function_name(input_value) == expected- Checks one generated claim against a concrete expected result.
assert function_name([]) == expected_empty- Tests the empty boundary a plausible first draft often misses.
assert function_name(data) == function_name(list(data))- Checks that equal inputs give equal answers without unexpected mutation.
git diff --check- Finds whitespace errors in a generated patch before reviewing its logic.
Debugging as a procedure
assert actual == expected, (actual, expected)- Stops at the first broken assumption and shows both values.
print(f"{index=}, {value=}")- Prints labelled state while tracing a small failing case.
breakpoint()- Pauses at the suspicious line so you can inspect live state.
pytest -k failing_test -x- Runs one failing test and stops immediately, keeping the feedback loop short.
Reading code you did not write Weeks 24–25
Getting your bearings
rg -n "def target_name" .- Finds a function definition before reading a codebase at random.
rg -n "target_name" .- Finds every call site, test and reference for a name.
git log --oneline -10- Shows the recent changes that explain what the project has been doing.
pytest -q- Establishes whether the current checkout already passes its tests.
Tracing one path end to end
rg -n "/api/orders" app lib- Finds the route or handler that receives one request path.
print(f"{request_id=}, {user_id=}")- Labels one request's values while following its path.
breakpoint()- Pauses at a hand-off to inspect exactly what crosses the boundary.
raise ValueError(f"unexpected state: {state!r}")- Stops a trace at an impossible state with its useful value attached.
Changing unfamiliar code safely
git status --short- Shows local changes before you begin, so you do not mix work together.
git diff -- path/to/file- Reviews only the file you intended to change.
pytest -q- Checks that the existing behaviour still passes after a small edit.
git diff --check- Catches trailing whitespace and conflict-marker mistakes before sharing a diff.
Reading a change, not just a diff
git diff- Shows your unstaged changes line by line.
git diff --stat- Summarises which files changed and by how much before the detail.
git diff --word-diff- Highlights the changed words inside a long line.
git show --stat HEAD- Summarises the latest commit before opening its full patch.
git diff --check- Finds whitespace errors and conflict markers in the diff.
Trees and graphs Weeks 26–29
Trees and binary trees
class Node: def __init__(self, value): self.value = value self.left = self.right = None- Defines a minimal binary-tree node.
if node is None: return- The base case for an empty child.
visit(node.value) walk(node.left) walk(node.right)- Preorder traversal: visit a node before its children.
walk(node.left) visit(node.value) walk(node.right)- Inorder traversal, which visits a BST in sorted order.
Binary search trees
if value < node.value: node = node.left else: node = node.right- Chooses the left or right subtree using the BST ordering rule.
while node is not None:- Searches down a tree until it finds a value or reaches an empty child.
if value == node.value: return node- Stops a BST search when the target is found.
node = node.left- Moves toward smaller values in a binary search tree.
Self-balancing trees, and why databases care
height = 1 + max(left_height, right_height)- Computes a node's height from its children.
balance = left_height - right_height- Computes the AVL balance factor for a node.
if abs(balance) > 1: node = rebalance(node)- Repairs an AVL node whose two sides differ too much in height.
return rotate_left(node)- Performs a rotation to shorten a right-heavy subtree.
Heaps
import heapq- Imports Python's min-heap tools.
heap = [3, 1, 4] heapq.heapify(heap)- Turns a list into a min-heap in place.
heapq.heappush(heap, item)- Adds an item while keeping the smallest item at heap[0].
smallest = heapq.heappop(heap)- Removes and returns the smallest item.
heapq.heappush(heap, (-priority, item))- Uses negative priorities when you need a max-heap.Tuples break ties with their next item. Add a counter if equal priorities contain values Python cannot compare.
Tries
node = {}- Uses a dictionary as one trie node, mapping characters to children.
node = node.setdefault(char, {})- Follows a character edge, creating it if it is new.
node["$"] = True- Marks that a complete word ends at this node.
for char in word: if char not in node: return False node = node[char]- Follows a word through a trie and fails as soon as a prefix is absent.
Graphs
graph = {"A": ["B", "C"], "B": []}- Represents a graph as an adjacency list.
graph[a].append(b)- Adds a directed edge from a to b.
graph[b].append(a)- Adds the reverse edge too, for an undirected graph.
for neighbour in graph[node]:- Visits each edge leaving a node.
Union-find
parent = list(range(n))- Starts n separate groups, each item its own parent.
def find(x): if parent[x] != x: parent[x] = find(parent[x]) return parent[x]- Finds a group's root and compresses the path for later calls.
root_a, root_b = find(a), find(b)- Finds the representative of each group before comparing or merging.
parent[root_b] = root_a- Merges two different groups by attaching one root to the other.
The classic algorithms Weeks 30–32
Binary search
low, high = 0, len(nums) - 1- Sets inclusive bounds for a sorted list.
mid = (low + high) // 2- Finds the middle index of the remaining range.
if nums[mid] < target: low = mid + 1 else: high = mid - 1- Discards the half that cannot contain the target.
import bisect pos = bisect.bisect_left(nums, target)- Finds where target belongs in a sorted list.
if pos < len(nums) and nums[pos] == target:- Checks that a bisect position really contains the target.bisect returns an insertion point even when the target is absent.
Sorting
nums.sort()- Sorts a list in place, ascending.
ordered = sorted(nums, reverse=True)- Returns a new list in descending order.
people.sort(key=lambda person: person.age)- Sorts objects by a chosen value.
words.sort(key=str.lower)- Sorts text without treating capitals separately.
pairs.sort(key=lambda pair: (pair[1], pair[0]))- Sorts by one field, then breaks ties with another.
Breadth-first and depth-first search
from collections import deque- Imports the queue used by breadth-first search.
queue = deque([start]) seen = {start} while queue: node = queue.popleft() for neighbour in graph[node]: if neighbour not in seen: seen.add(neighbour); queue.append(neighbour)- The BFS shape: visit graph nodes one distance layer at a time.
stack = [start] seen = set() while stack: node = stack.pop() if node not in seen: seen.add(node); stack.extend(graph[node])- An iterative DFS, which goes deep along one path before backtracking.
seen.add(start)- Marks a node when it enters the frontier, preventing duplicate queue work.
Shortest paths
dist = {node: float("inf") for node in graph} dist[start] = 0- Initialises every distance as unknown, except the starting point.
import heapq pq = [(0, start)]- Starts Dijkstra's priority queue with the source at distance zero.
cost, node = heapq.heappop(pq)- Takes the next node with the cheapest known route.
if cost + weight < dist[neighbour]: dist[neighbour] = cost + weight heapq.heappush(pq, (dist[neighbour], neighbour))- Relaxes an edge when it improves a neighbour's best route.
if cost != dist[node]: continue- Skips an old heap entry after a better route was found.Dijkstra is not correct when any edge has a negative weight.
Minimum spanning trees
edges.sort(key=lambda edge: edge[2])- Orders weighted edges from cheapest to most expensive for Kruskal's algorithm.
if find(a) != find(b):- Checks whether an edge would join two separate components.
union(a, b) mst.append((a, b, weight))- Accepts an edge and merges its two components.
if len(mst) == len(vertices) - 1: break- Stops once the spanning tree has enough edges.
Topological sort
from graphlib import TopologicalSorter- Imports the standard-library tool for dependency ordering.
order = list(TopologicalSorter(graph).static_order())- Returns a valid order for a dependency graph.
indegree[child] += 1- Counts one prerequisite edge into child.
if indegree[neighbour] == 0: queue.append(neighbour)- Queues work once every prerequisite has been removed.
if len(order) != len(graph): raise ValueError("cycle")- Detects a cycle when Kahn's algorithm cannot order every node.
Choosing the right approach Weeks 33–35
Brute force
for left in range(len(nums)): for right in range(left + 1, len(nums)): if nums[left] + nums[right] == target: return left, right- Checks every pair, a clear first solution for two-sum.
from itertools import combinations- Imports a generator for choosing groups without writing nested loops.
for pair in combinations(nums, 2):- Visits every two-item combination.
best = min(candidates, key=score)- Chooses the best answer after trying every candidate.
Divide and conquer
mid = len(items) // 2- Finds a midpoint for splitting a problem in two.
left = solve(items[:mid]) right = solve(items[mid:])- Solves two smaller halves recursively.
return combine(left, right)- Combines the two solved halves into one answer.
if len(items) <= 1: return items- Stops when the problem is already small enough to answer directly.
Greedy algorithms
intervals.sort(key=lambda interval: interval[1])- Orders intervals by earliest finishing time for interval scheduling.
if start >= last_end: chosen.append((start, end)) last_end = end- Takes the next compatible interval after choosing the earliest finish.
for coin in sorted(coins, reverse=True):- Considers the largest available coin first.
take = min(remaining // coin, available)- Takes as much of a locally best choice as the constraints allow.
Backtracking
def search(path): if complete(path): answers.append(path.copy()) return- Ends one branch when it forms a complete answer.
for choice in choices(path): path.append(choice) search(path) path.pop()- The choose, explore, un-choose shape for exhaustive search.
if not valid(path): return- Prunes a branch as soon as it cannot lead to a valid answer.
answers.append(path.copy())- Saves the current answer without sharing the mutable path.Appending path itself saves repeated references to one list, which later pops will empty.
Dynamic programming
from functools import cache- Imports memoisation for a top-down dynamic programme.
@cache def ways(n): if n <= 1: return 1 return ways(n - 1) + ways(n - 2)- Turns repeated recursive subproblems into one cached result each.
dp = [0] * (n + 1)- Allocates a table with one answer slot per subproblem.
for i in range(2, n + 1): dp[i] = dp[i - 1] + dp[i - 2]- Builds answers bottom-up after their dependencies exist.
dp = [[0] * cols for _ in range(rows)]- Creates independent rows for a two-dimensional table.[[0] * cols] * rows repeats the same row object, so one write appears in every row.
Two pointers, sliding windows and prefix sums
left, right = 0, len(nums) - 1- Starts two pointers at opposite ends of a sorted list.
while left < right: total = nums[left] + nums[right] if total < target: left += 1 else: right -= 1- Moves the useful pointer after comparing a pair sum with the target.
left = 0 for right, value in enumerate(nums):- Grows a sliding window one item at a time.
while window_sum > limit: window_sum -= nums[left] left += 1- Shrinks a window until it satisfies its constraint.
Bit manipulation
n & 1- Reads the last bit: 1 for odd n, 0 for even n.
n >> 1- Shifts bits right by one, like floor-dividing a non-negative integer by two.
n | (1 << bit)- Sets a numbered bit to 1.
n & ~(1 << bit)- Clears a numbered bit to 0.
n ^ (1 << bit)- Flips a numbered bit.
n & (n - 1)- Clears the lowest set bit, useful for counting set bits.
Optional depth Week 36 and beyond
Segment trees and Fenwick trees
size = 1 while size < n: size *= 2- Rounds the leaf capacity up to a power of two.
tree = [0] * (2 * size)- Allocates an iterative segment tree with leaves after index size.
tree[size + index] = value- Stores one input value in its leaf position.
tree[index] = tree[2 * index] + tree[2 * index + 1]- Combines two child ranges into their parent range.
String search algorithms
pattern in text- Checks whether a substring occurs anywhere in text.
text.find(pattern)- Returns the first match position, or -1 when there is none.
from collections import Counter- Imports a frequency table for character-count comparisons.
Counter(word) == Counter(candidate)- Tests whether two words are anagrams.
prefix = [0] * len(pattern)- Starts the prefix table used by Knuth-Morris-Pratt matching.
Randomised and approximate algorithms
import random- Imports Python's ordinary pseudo-random tools.
random.shuffle(items)- Randomly rearranges a list in place.
choice = random.choice(items)- Selects one item uniformly from a non-empty sequence.
sample = random.sample(items, k=3)- Selects distinct items without replacement.
from secrets import token_urlsafe token = token_urlsafe(32)- Creates a security-sensitive random token.random is predictable enough for simulations and games, not passwords, reset links or tokens.