Data Structures & Algorithms Cheatsheet
Arrays and Strings
Use this Data Structures & Algorithms reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Complexity
A dynamic array stores elements contiguously, so indexing is O(1) and inserting in the middle shifts everything after it.
| Operation | Array / list | Notes |
|---|---|---|
| index read or write | O(1) | contiguous memory, cache friendly |
| append at end | O(1) amortized | doubling reallocation |
| pop from end | O(1) | |
| insert or delete at index i | O(n − i) | shifts the tail |
| insert or delete at front | O(n) | use a deque instead |
| search unsorted | O(n) | |
| search sorted | O(log n) | binary search |
| slice of length k | O(k) | copies in Python and Java |
| reverse in place | O(n) time, O(1) space | |
| concatenate a + b | O(n + m) |
Space is O(n) for the array itself, with capacity typically between n and 2n.
Python Array Toolkit
a = [3, 1, 2] a.append(4) # end insert, O(1) amortized a.extend([5, 6]) # O(k) a.insert(0, 9) # O(n) a.pop() # last, O(1) a.pop(0) # first, O(n) -- prefer deque a.remove(3) # first occurrence by value, O(n) del a[1:3] # delete a slice a.index(2) # first index, ValueError if absent a.count(2) a.sort() # in place, stable, O(n log n) b = sorted(a, key=abs, reverse=True) # new list a.reverse() # in place a[::-1] # reversed copy a[::2] # every other element sum(a), min(a), max(a), len(a) [x * 2 for x in a if x > 1] # comprehension list(enumerate(a)) # (index, value) pairs list(zip(a, b)) # pairwise, stops at shorter [[0] * cols for _ in range(rows)] # correct 2D grid
[[0] * cols] * rowsaliases the same inner listrowstimes. Writingg[0][0]changes every row. Always use the comprehension form.
Two-Dimensional Grids
rows, cols = len(g), len(g[0]) for r in range(rows): for c in range(cols): ... # 4-directional neighbors, bounds-checked DIRS = ((1, 0), (-1, 0), (0, 1), (0, -1)) for dr, dc in DIRS: nr, nc = r + dr, c + dc if 0 <= nr < rows and 0 <= nc < cols: ... # transpose t = [list(row) for row in zip(*g)] # rotate 90 degrees clockwise: transpose, then reverse each row rot = [list(row)[::-1] for row in zip(*g)] # flatten index math for a rows x cols grid idx = r * cols + c r, c = divmod(idx, cols)
In-Place Patterns
Interview arrays are usually mutated in place with O(1) extra space.
def reverse(a): i, j = 0, len(a) - 1 while i < j: a[i], a[j] = a[j], a[i] i += 1 j -= 1 def remove_val(a, val): """Overwrite pointer: keep everything != val, return new length.""" k = 0 for x in a: if x != val: a[k] = x k += 1 return k def rotate_right(a, k): """Reverse whole, then reverse each part. O(n) time, O(1) space.""" n = len(a) k %= n a.reverse() a[:k] = reversed(a[:k]) a[k:] = reversed(a[k:]) def dutch_flag(a): """Sort 0s, 1s, 2s in one pass.""" lo, mid, hi = 0, 0, len(a) - 1 while mid <= hi: if a[mid] == 0: a[lo], a[mid] = a[mid], a[lo] lo += 1 mid += 1 elif a[mid] == 2: a[mid], a[hi] = a[hi], a[mid] hi -= 1 else: mid += 1
Strings
Strings are immutable in Python, Java, and C#, so every "modification" allocates a new string.
| Task | Python | Cost |
|---|---|---|
| build in a loop | parts.append(x) then "".join(parts) | O(n) |
naive s += x in a loop | avoid | O(n²) |
| substring | s[i:j] | O(j − i) |
| find | s.find(t), -1 if absent | O(n·m) worst |
| contains | t in s | O(n·m) worst |
| split and join | s.split(","), ",".join(xs) | O(n) |
| strip | s.strip(), .lstrip(), .rstrip() | O(n) |
| case | s.lower(), .upper(), .casefold() | O(n) |
| pad | s.zfill(4), s.rjust(8, "0") | O(n) |
| replace | s.replace("a", "b") | O(n) |
| tests | .isalpha() .isdigit() .isalnum() | O(n) |
| char codes | ord("a"), chr(97) | O(1) |
| translate | s.translate(str.maketrans("ab", "xy")) | O(n) |
# a-z frequency without a dict cnt = [0] * 26 for ch in s: cnt[ord(ch) - ord("a")] += 1 # anagram signature from collections import Counter Counter(s) == Counter(t) sorted(s) == sorted(t) # O(n log n) alternative # palindrome check ignoring non-alphanumerics def is_palindrome(s): i, j = 0, len(s) - 1 while i < j: while i < j and not s[i].isalnum(): i += 1 while i < j and not s[j].isalnum(): j -= 1 if s[i].lower() != s[j].lower(): return False i += 1 j -= 1 return True
Java And C++ Equivalents
Idioms diverge most on string building and array sizing.
// Java: StringBuilder for O(n) construction StringBuilder sb = new StringBuilder(); for (char c : s.toCharArray()) sb.append(Character.toLowerCase(c)); String out = sb.reverse().toString(); int[] a = new int[n]; // zero-filled, fixed length Arrays.fill(a, -1); Arrays.sort(a); // dual-pivot quicksort on primitives int[][] g = new int[rows][cols]; List<Integer> list = new ArrayList<>(); list.add(1); list.get(0); list.size(); int[] copy = Arrays.copyOfRange(a, 1, 4);
// C++: vector plus string, both mutable and growable vector<int> a(n, 0); a.push_back(5); sort(a.begin(), a.end()); reverse(a.begin(), a.end()); vector<vector<int>> g(rows, vector<int>(cols, 0)); string s = "abc"; s += 'd'; // O(1) amortized, string is mutable here s.substr(1, 2); // "bc" s.find("bc"); // string::npos if absent
Arrays.sortonint[]in Java is quicksort and can hit O(n²) on crafted input. OnInteger[]or with a comparator it is a stable merge sort. Boxing toInteger[]is the standard defense in contests.
Gotchas
- Negative indices. Python
a[-1]is the last element. Java and C++ do not wrap,a[-1]is out of bounds or undefined behavior. - Slice bounds never throw in Python.
a[5:99]on a 3-element list returns[]rather than raising. - Sorting is stable in Python, Java objects, and
std::stable_sort. Plainstd::sortand Java primitive sort are not stable. - Integer division. Python
//floors toward negative infinity (-7 // 2 == -4), Java and C++ truncate toward zero (-7 / 2 == -3). - Overflow. Java
intand C++intwrap at 2³¹ − 1. Uselongfor sums andlo + (hi - lo) / 2for midpoints. Python integers are arbitrary precision. - Mutating while iterating a list skips elements. Iterate over a copy or build a new list.