Skip to main content

Python and Algorithm Cheat Sheet

Quick reference for solving algorithm problems in Python.

Python Cheat Sheet

Small language tricks that save time.

Backtracking2

Copy a list

list(path) or path[:] both create a copy of the path. Useful in backtracking problems: appending path itself stores a reference, so later changes to path would change the saved answer too.

Copy a list
Python
1result.append(path[:]) # or: result.append(list(path))

Deduplicate lists with a set of tuples

A list can't go in a set (it isn't hashable), but a tuple can. Store each path as a tuple to drop duplicates, then turn the tuples back into lists when returning.

Deduplicate lists with a set of tuples
Python
1result = set()
2# To add a list path:
3result.add(tuple(path))
4 
5# To return a list from a set
6return [list(item) for item in result]

Lists2

Last element: path[-1]

In Python, path[-1] refers to the last element of the list path.

Last element: path[-1]
Python
1path = [1, 4, 6]
2path[-1] # 6

Empty-list check: if path:

In Python, the check if path: evaluates whether the path list contains any items.

Empty-list check: if path:
Python
1path = []
2if path: # [] is falsy: skipped
3 print(path[-1])
4 
5path = [4]
6if path: # [4] is truthy: runs
7 print(path[-1]) # 4
PracticeLC 491Non Decreasing Subsequences

Truthiness1

Integer truthiness: if run:

In Python, if run: will evaluate to False only for 0. Negative numbers evaluate to True.

Integer truthiness: if run:
Python
1run = 0
2if run:
3 print("This will NOT print because 0 is False.")
4 
5run = -1
6if run:
7 print("This WILL print because -1 is True.")
8 
9run = 5
10if run:
11 print("This WILL print because 5 is True.")

If you specifically want to check if a number is strictly greater than zero (ignoring both 0 and negative numbers), you must write the explicit comparison: if run > 0:.

Syntax2

One-line if: a if condition else b

In Python you can write an if on one line almost anywhere a value is expected: a if condition else b gives a when the condition is truthy and b otherwise. It works inside function arguments, string building, return, list comprehensions and assignments.

One-line if: a if condition else b
Python
1# Inside a recursive call (Generalized Abbreviation):
2# append the count only when it is non-zero
3backtrack(i + 1, path + (str(run) if run else "") + word[i], 0)
4 
5# Pick one of two values
6step = 1 if ch == "(" else -1
7best = a if a > b else b
8 
9# Turn "not found" into -1 on return
10return dist if dist != float("inf") else -1
11 
12# Inside a list comprehension
13labels = ["even" if x % 2 == 0 else "odd" for x in nums]

Use it for choosing between two values. If a branch needs to run statements (append, pop, recurse), write a normal if block.

PracticeLC 320Generalized Abbreviation

No line break right after =

In Python, you cannot press Enter immediately after an equals sign (=) to start an expression on the next line without using a line continuation character. Because the opening curly brace { is on the next line, Python thinks the lettermap = statement is incomplete and will throw a SyntaxError.

No line break right after =
Python
1# SyntaxError: invalid syntax
2lettermap =
3{
4 "2": "abc",
5}
6 
7# Fixed: open the brace on the = line
8lettermap = {
9 "2": "abc",
10 "3": "def",
11}

Sets12

Create a set

A set holds unique, unordered items. Build an empty one with set(), never {} (that is an empty dict). Passing any iterable to set(...) drops duplicates.

Create a set
Python
1s = set() # empty set
2d = {} # careful: this is an empty DICT
3s = {1, 2, 3} # literal
4s = set([3, 1, 3, 2]) # {1, 2, 3} duplicates dropped
5s = set("hello") # {'h', 'e', 'l', 'o'}
6s = {x * x for x in range(4)} # set comprehension: {0, 1, 4, 9}

Add items: add, update

add(x) inserts one item; adding something already there does nothing. update(...) (or |=) adds every item from one or more iterables.

Add items: add, update
Python
1s = {1, 2}
2s.add(3) # {1, 2, 3}
3s.add(3) # still {1, 2, 3}, no error
4s.update([4, 5], (6,)) # {1, 2, 3, 4, 5, 6}
5s |= {7} # same as s.update({7})

Remove items: remove, discard, pop, clear

remove(x) raises KeyError if x is missing; discard(x) does nothing instead, so reach for it when you are not sure the item is there. pop() removes and returns an arbitrary item (not the smallest, not the last added).

Remove items: remove, discard, pop, clear
Python
1s = {1, 2, 3}
2s.remove(2) # {1, 3}
3s.remove(99) # KeyError: 99
4s.discard(99) # no error, s unchanged
5x = s.pop() # removes and returns some item
6s.clear() # set()
7set().pop() # KeyError: 'pop from an empty set'

Check membership: in

x in s is O(1) on average, versus O(n) for x in some_list. When a loop keeps asking "have I seen this?", turn the list into a set once, before the loop.

Check membership: in
Python
1seen = {3, 5, 8}
25 in seen # True
34 not in seen # True
4 
5allowed = set(words) # O(n) once...
6for w in queries:
7 if w in allowed: # ...then O(1) per lookup
8 ...

Get an item (sets have no index)

s[0] raises TypeError: sets are unordered, so there is no first or i-th item. Iterate, peek with next(iter(s)), or sort when you need an order.

Get an item (sets have no index)
Python
1s = {10, 20, 30}
2s[0] # TypeError: 'set' object is not subscriptable
3x = next(iter(s)) # peek at some item without removing it
4x = next(iter(s), None) # same, but None when s is empty
5for x in s: ... # visit every item (order not guaranteed)
6min(s), max(s) # 10, 30 O(n)
7sorted(s) # [10, 20, 30] a new list

Set operations: union, intersection, difference

Operators need both sides to be sets; the method forms (a.union(...), a.intersection(...)) accept any iterable. The |=, &=, -=, ^= forms update a in place.

Set operations: union, intersection, difference
Python
1a, b = {1, 2, 3}, {2, 3, 4}
2a | b # {1, 2, 3, 4} union: in either
3a & b # {2, 3} intersection: in both
4a - b # {1} difference: in a, not in b
5a ^ b # {1, 4} symmetric difference: in exactly one
6a.union([5]) # method form takes any iterable
7 
8{1, 2} <= a # True subset (a.issubset)
9a >= {1, 2} # True superset (a.issuperset)
10{1, 2} < a # True proper subset (not equal)
11a.isdisjoint({7, 8}) # True no items in common
12 
13a &= {2, 3, 9} # in place: a is now {2, 3}
PracticeLC 349

Deduplicate and detect duplicates

Comparing len(set(nums)) with len(nums) tells you whether anything repeats. list(set(nums)) removes duplicates but loses the original order; list(dict.fromkeys(nums)) keeps it.

Deduplicate and detect duplicates
Python
1nums = [3, 1, 3, 2, 1]
2len(set(nums)) != len(nums) # True: has duplicates
3list(set(nums)) # [1, 2, 3] in some order
4list(dict.fromkeys(nums)) # [3, 1, 2] original order kept
PracticeLC 217Contains Duplicate

What can go in a set

Only hashable (immutable) values: numbers, strings, tuples. Lists, dicts and sets cannot go in a set. Convert a list to a tuple, and use frozenset when you need a set of sets.

What can go in a set
Python
1{[1, 2]} # TypeError: unhashable type: 'list'
2{(0, 1), (2, 3)} # tuples are fine: grid cells, pairs
3{tuple(path)} # store a path as a tuple
4{{1}} # TypeError: unhashable type: 'set'
5groups = {frozenset({1, 2})} # frozenset is hashable
6frozenset({2, 1}) in groups # True: order does not matter

Don't change a set while looping over it

Adding or removing items while a for loop walks the same set raises RuntimeError: Set changed size during iteration. Loop over a copy, or build a new set.

Don't change a set while looping over it
Python
1for x in s:
2 if x % 2 == 0:
3 s.discard(x) # RuntimeError
4 
5for x in list(s): # loop over a copy
6 if x % 2 == 0:
7 s.discard(x) # fine
8 
9s = {x for x in s if x % 2} # or build a new set

The seen set: check, then add

The most common set pattern: walk the input, check whether you have met the item before, then record it. Loading everything into a set up front also makes "does n - 1 exist?" an O(1) question, which is the trick behind Longest Consecutive Sequence.

The seen set: check, then add
Python
1# Duplicate check
2seen = set()
3for x in nums:
4 if x in seen:
5 return True
6 seen.add(x)
7return False
8 
9# Longest Consecutive Sequence: only start counting at a run's first number
10num_set = set(nums)
11best = 0
12for n in num_set:
13 if n - 1 not in num_set: # n starts a run
14 length = 1
15 while n + length in num_set:
16 length += 1
17 best = max(best, length)
PracticeLC 217Contains DuplicateLC 128Longest Consecutive SequenceLC 202Happy Number

Visited and used sets in backtracking and grids

Backtracking: add before the recursive call and remove right after, so the set only holds what is on the current path. A fresh used set created inside the call skips repeated values at the same depth. Grids: store cells as (r, c) tuples.

Visited and used sets in backtracking and grids
Python
1# Permutations: indices on the current path
2if i in used:
3 continue
4used.add(i)
5path.append(nums[i])
6backtrack()
7path.pop()
8used.remove(i)
9 
10# Same-depth dedup (Non-decreasing Subsequences, Permutations II)
11used = set() # new set for this call only
12for i in range(start, len(nums)):
13 if nums[i] in used:
14 continue
15 used.add(nums[i])
16 ...
17 
18# Grid: cells as tuples
19visited = {(0, 0)}
20if (r, c) not in visited:
21 visited.add((r, c))
PracticeLC 46PermutationsLC 47Permutations IILC 491Non Decreasing SubsequencesLC 200Number Of Islands

Cost of set operations

Average-case costs (hash collisions can make the worst case slower).

Cost of set operations
Python
1x in s, s.add(x), s.remove(x), s.discard(x) # O(1)
2set(iterable) # O(n)
3a | b # O(len(a) + len(b))
4a & b # O(min(len(a), len(b)))
5a - b # O(len(a))
6a <= b # O(len(a))
7min(s), max(s) # O(n)
8sorted(s) # O(n log n)

Characters7

ord and chr: characters as numbers

ord(c) gives a character's code number and chr(n) turns the number back into a character. Letters are consecutive, so 'a' to 'z' are 97 to 122 and 'A' to 'Z' are 65 to 90. That is what makes letter arithmetic work.

ord and chr: characters as numbers
Python
1ord("a") # 97
2ord("z") # 122
3ord("A") # 65
4ord("0") # 48
5chr(97) # 'a'
6chr(65) # 'A'

Map a letter to an index 0-25

Subtract ord("a") to turn a lowercase letter into a position: 'a' is 0, 'b' is 1, ..., 'z' is 25. Add it back with chr to go from a position to a letter.

Map a letter to an index 0-25
Python
1idx = ord(c) - ord("a") # 'a' -> 0, 'd' -> 3, 'z' -> 25
2c = chr(idx + ord("a")) # 3 -> 'd'
3 
4letters = [chr(ord("a") + i) for i in range(26)] # ['a', ..., 'z']
5# or: import string; string.ascii_lowercase

Count letters with a 26-slot array

When the input is only lowercase letters, a list of 26 zeros is a small, fast counter: slot ord(c) - ord("a") holds how many times c appears. Two strings are anagrams when their count arrays are equal. For any character (spaces, digits, capitals), use 128 slots and index with ord(c) directly.

Count letters with a 26-slot array
Python
1count = [0] * 26
2for c in s:
3 count[ord(c) - ord("a")] += 1
4# "banana" -> count[0] == 3 ('a'), count[13] == 2 ('n')
5 
6# Valid Anagram: +1 for s, -1 for t, all zeros at the end
7count = [0] * 26
8for a, b in zip(s, t):
9 count[ord(a) - ord("a")] += 1
10 count[ord(b) - ord("a")] -= 1
11return all(x == 0 for x in count)
12 
13# First unique character: count, then find the first count of 1
14for i, c in enumerate(s):
15 if count[ord(c) - ord("a")] == 1:
16 return i
17 
18# Any ASCII character
19count = [0] * 128
20count[ord(c)] += 1

collections.Counter(s) does the same job for any characters; the fixed array is handy when you need to compare counts (==) or use them as a key.

PracticeLC 242Valid AnagramLC 387First Unique Character In A StringLC 383Ransom Note

Use the count array as a dict key

Words with the same letter counts are anagrams of each other. A list can't be a dict key, so convert the 26 counts to a tuple and group by it. This avoids sorting each word.

Use the count array as a dict key
Python
1from collections import defaultdict
2 
3groups = defaultdict(list)
4for word in strs:
5 key = [0] * 26
6 for c in word:
7 key[ord(c) - ord("a")] += 1
8 groups[tuple(key)].append(word)
9return list(groups.values())
10# ["eat","tea","tan","ate","nat","bat"] -> [["eat","tea","ate"], ["tan","nat"], ["bat"]]
PracticeLC 49Group Anagrams

Sliding window with two count arrays

To find every anagram of p inside s, keep one count array for p and one for the current window. Add the letter entering the window, subtract the one leaving, and compare the two arrays (26 slots, so each comparison is constant time).

Sliding window with two count arrays
Python
1need, window = [0] * 26, [0] * 26
2for c in p:
3 need[ord(c) - ord("a")] += 1
4 
5res = []
6for i, c in enumerate(s):
7 window[ord(c) - ord("a")] += 1 # letter enters
8 if i >= len(p):
9 window[ord(s[i - len(p)]) - ord("a")] -= 1 # letter leaves
10 if window == need:
11 res.append(i - len(p) + 1)
12# s = "cbaebabacd", p = "abc" -> [0, 6]
PracticeLC 438Find All Anagrams In A StringLC 567Permutation In String

Letter arithmetic: shifts, digits, case

Because codes are consecutive you can do math on characters: rotate a letter with % 26, read a digit character as a number, or change case (lowercase is exactly 32 above uppercase).

Letter arithmetic: shifts, digits, case
Python
1# Shift a letter by k, wrapping around (Caesar cipher)
2chr((ord(c) - ord("a") + k) % 26 + ord("a")) # 'z' + 1 -> 'a'
3 
4# Digit character to int (same as int(c))
5ord("7") - ord("0") # 7
6num = 0
7for c in "305":
8 num = num * 10 + (ord(c) - ord("0")) # 305
9 
10# Case: lowercase = uppercase + 32
11ord("a") - ord("A") # 32
12chr(ord("q") - 32) # 'Q' (or c.upper())
PracticeLC 8String To Integer Atoi

Letters as a bitmask

Set bit ord(c) - ord("a") for every letter in a word, and the whole word's letter set fits in one integer. Two words share no letters exactly when mask1 & mask2 == 0, a single operation instead of comparing strings.

Letters as a bitmask
Python
1def mask(word):
2 m = 0
3 for c in word:
4 m |= 1 << (ord(c) - ord("a"))
5 return m
6 
7mask("abc") & mask("def") == 0 # True: no shared letters
8mask("abc") & mask("cat") != 0 # True: share 'a' and 'c'
9bin(mask("abca")).count("1") # 3 distinct letters
PracticeLC 318