Skip to content
dsa/roadmap
Cheat sheets

Revise on one page

Every chapter's costs, top mistakes and terms. Print one chapter, or print this page for all eight.

01

Introduction to Data Structures

dsa.fetehadin.com
1.1 What Is a Data Structure?

A data structure is a way of organising data in memory so the operations you need are fast. Learn how they are classified and what makes a good one.

Array: read item iO(1)
Array: insert at the frontO(n): shift everything
Linked list: insert at the frontO(1)
Linked list: read item iO(n): walk from the head
  • ✗Calling a stack "non-linear". Stacks and queues are linear; only trees and graphs are non-linear here.
  • ✗Saying an array is dynamic. A C++ array has a fixed size; vector and linked lists are dynamic.
  • ✗Confusing a data structure (how data is stored) with an algorithm (the steps that use it).
1.2 ADTs & Abstraction

An Abstract Data Type says WHAT operations exist; a data structure says HOW they are built. Separating the two is the key idea of this course.

push / pop / top (array implementation)O(1)
push / pop / top (linked implementation)O(1)
Maximum sizearray: fixed · linked list: limited only by memory
  • ✗Saying an ADT "is" an array. An ADT has no storage details at all; an array can only implement one.
  • ✗Listing implementation details (like top index) as part of the ADT's definition.
  • ✗Forgetting error conditions. "pop on an empty stack is an error" IS part of the ADT.
1.3 Arrays & 2-D Arrays (Review)

Arrays store items side by side in memory, which is why reading any index is instant. 2-D arrays are stored row by row.

Access a[i]O(1)
Search (unsorted)O(n)
Insert / delete at position iO(n): shift items
Insert / delete at the endO(1) if there is space
Traverse an R × C matrixO(R·C)
SpaceO(n) for n items
  • ✗Using 1-based indexes. C++ arrays start at 0, so the last valid index is n − 1.
  • ✗Writing a[n] (out of bounds). C++ does not check, so it silently corrupts memory.
  • ✗Using sizeof(a)/sizeof(a[0]) inside a function. There, a is a pointer, so the trick gives the wrong size. Pass n instead.
Data structure:
A way of organising data in memory so that the operations you need are efficient.
Primitive data structure:
Built-in types operated on directly by the machine: int, char, float, bool, pointers.
Linear data structure:
Items form a sequence, each with at most one predecessor and one successor: arrays, linked lists, stacks, queues.
Non-linear data structure:
Items branch or connect freely: trees and graphs.
Static vs dynamic:
Static structures have a size fixed when they are created (arrays); dynamic ones grow and shrink at run time (linked lists).
ADT (Abstract Data Type):
A description of data plus the operations on it, saying what they do but not how they are implemented.
Abstraction:
Hiding implementation details behind an interface so users only see what they need.
Row-major order:
How C++ stores a 2-D array: row 0 first, then row 1. Address of a[i][j] = base + (i·cols + j)·size.
02

Algorithm & Algorithm Analysis

dsa.fetehadin.com
2.1 Algorithms & Their Properties

An algorithm is a finite, unambiguous sequence of steps that solves a problem. Learn the 11 properties every correct algorithm must have.

findMax: comparisonsexactly n − 1
findMax: timeO(n), and best = worst
SpaceO(1)
  • ✗Confusing definiteness (each step has one clear meaning) with finiteness (the algorithm stops).
  • ✗Saying an algorithm needs at least one input. It needs zero or more inputs but at least one output.
  • ✗Treating "a program" and "an algorithm" as the same. A program may run forever (an OS); an algorithm must terminate.
2.2 Measuring Efficiency

Empirical vs theoretical analysis, qualitative vs quantitative, the three classic avoidable inefficiencies, and the time–space trade-off.

Search with late terminationalways n checks
Search that breaks early1 … n checks
Duplicate check with nested loopsO(n²) time, O(1) space
Duplicate check with a setO(n) time, O(n) space ← trade-off
  • ✗Saying empirical analysis is useless. It is useful, but its results depend on the machine, compiler, language and inputs.
  • ✗Mixing up "redundant computation" (recomputing the same value in a loop) with "repeated array referencing" (reading a[i] many times).
  • ✗Thinking the faster algorithm is always better. It may need far more memory.
2.3 Counting Time Units

Turn code into a formula T(n) using the instructor's counting rules. Loops become sums, and nested loops multiply.

Single loop for(i=0;i<n;i++) k=k+1; cout<<k;T(n) = 4n + 3 → O(n)
Nested n × n loop with k++T(n) = 3n² + 4n + 3 → O(n²)
Loop where i doubles (i *= 2)≈ log₂ n iterations → O(log n)
  • ✗Counting the loop test n times instead of n + 1 (the last, failing test counts too).
  • ✗Counting k = k + 1 as 1 unit. It is 2: one addition and one assignment.
  • ✗For if/else, adding both branches. Count the condition plus the larger branch only.
2.4 Best, Average & Worst Case

The same algorithm can be fast or slow depending on the input. We analyse the best, average and worst case, and usually report the worst.

Linear search: best (key is first)O(1): 1 comparison
Linear search: average (key present)(n + 1) / 2 comparisons → O(n)
Linear search: worst (last or missing)n comparisons → O(n)
Bubble sort with flag: best / worstO(n) / O(n²)
SpaceO(1)
  • ✗"Best case is when n is small." No: best case is about the arrangement of the input for a given n, never about a small n.
  • ✗Thinking the average case is (best + worst) / 2. It is an expected value over all inputs.
  • ✗Mixing up the case with the notation. "Worst case" is not the same thing as "Big-O".
2.5 Asymptotic Notation & Big-O Proofs

O, Ω, Θ, o and ω describe how fast T(n) grows for large n. Learn the definitions, the order of growth, and how to prove f(n) = O(g(n)) by finding c and k.

O(1)constant: array access, push/pop
O(log n)logarithmic: binary search
O(n)linear: linear search, traversal
O(n log n)linearithmic: merge sort, heap sort
O(n²)quadratic: bubble, selection, insertion sort
O(n³)cubic: three nested loops
O(2ⁿ)exponential: all subsets, naive Fibonacci
  • ✗Forgetting to state c and k explicitly. A proof without numbers gets partial marks.
  • ✗Claiming 3n² = O(n). False: no constant c makes 3n² ≤ c·n for all large n.
  • ✗Thinking Big-O means "worst case". Big-O is an upper bound on a function; you can give an upper bound for the best case too.
Algorithm:
A finite, well-defined sequence of steps that turns input into the desired output.
Finiteness:
An algorithm must stop after a finite number of steps.
Definiteness:
Every step is precise and unambiguous.
Empirical analysis:
Measuring an algorithm by running it and timing it. Depends on the machine and the input.
Theoretical analysis:
Counting basic operations as a function of input size n. Machine-independent.
Time unit:
One basic operation (assignment, single I/O, Boolean or arithmetic op, return) counted as 1 in T(n).
T(n):
The running time of an algorithm as a function of input size n, in time units.
Best / average / worst case:
The running time on the most favourable, typical, and least favourable input of size n.
Big-O (O):
Upper bound: f(n) = O(g(n)) if f(n) ≤ c·g(n) for all n ≥ k, for some constants c > 0 and k.
Big-Omega (Ω):
Lower bound: f(n) ≥ c·g(n) for all n ≥ k.
Big-Theta (Θ):
Tight bound: f(n) is both O(g(n)) and Ω(g(n)).
Little-o / little-omega:
Strict bounds: f grows strictly slower (o) or strictly faster (ω) than g.
Time/space trade-off:
Using more memory to save time (or the reverse), e.g. storing results instead of recomputing them.
03

Simple Sorting & Searching

dsa.fetehadin.com
3.1 Selection Sort

Find the smallest remaining item and swap it to the front. Repeat. Simple, at most n − 1 swaps, but always O(n²) comparisons.

Best caseO(n²): it still scans for the minimum every pass
Average caseO(n²)
Worst caseO(n²): n(n−1)/2 comparisons
Swapsat most n − 1 (the fewest of the simple sorts)
SpaceO(1)
Stable · in-placeno · yes
  • ✗Swapping inside the inner loop every time a smaller item is found. Swap once, after the inner loop.
  • ✗Starting the inner loop at j = i instead of j = i + 1 (compares an item with itself).
  • ✗Claiming selection sort has an O(n) best case. It has none; it always scans.
3.2 Bubble Sort

Compare neighbours and swap them if they are out of order. After each pass the biggest remaining item has "bubbled" to the end.

Best case (already sorted)O(n): one pass, no swaps, stop
Average caseO(n²)
Worst case (reversed)O(n²): n(n−1)/2 comparisons and swaps
SpaceO(1)
Stable · in-placeyes · yes
  • ✗Looping j up to n - 1 in every pass instead of n - 1 - i. It still works, but does useless comparisons.
  • ✗Writing a[j] >= a[j+1]. Swapping equal items breaks stability.
  • ✗Reading past the array with j < n and then accessing a[j + 1].
3.3 Insertion Sort

Grow a sorted part on the left by inserting each new item into its correct place, like sorting cards in your hand. Fast on nearly sorted data.

Best case (already sorted)O(n): n − 1 comparisons, 0 shifts
Average caseO(n²)
Worst case (reversed)O(n²): n(n−1)/2 comparisons and shifts
SpaceO(1)
Stable · in-placeyes · yes
  • ✗Writing a[j] >= key in the while loop. That breaks stability.
  • ✗Forgetting j >= 0 in the loop condition, which reads a[-1].
  • ✗Putting a[j + 1] = key inside the while loop. It goes after the loop.
3.4 Pointer Sort

When records are big, don't move them. Sort an array of pointers (or indices) to the records instead, and only small addresses get swapped.

Comparisonssame as the underlying sort (here selection: n(n−1)/2)
Data moved per swapone pointer (8 bytes) instead of one whole record
TimeO(n²) with selection sort; O(n log n) with a fast sort
SpaceO(n) for the pointer array
Stable · in-placeno · no
  • ✗Swapping the records (swap(*p[i], *p[min])) instead of the pointers (swap(p[i], p[min])). That defeats the purpose.
  • ✗Comparing the pointers themselves (p[j] < p[min]) instead of what they point to (p[j]->score).
  • ✗Forgetting to initialise p[i] = &s[i] before sorting.
3.5 Linear Search

Check every item from left to right until you find the key or run out. Works on any array, sorted or not, but costs O(n).

Best caseO(1): key at index 0
Average case (found)(n + 1)/2 comparisons → O(n)
Worst caseO(n): last item or not present
Needs sorted data?No
SpaceO(1)
  • ✗Returning -1 inside the loop (in an else), which stops after checking only the first item.
  • ✗Looping with i <= n, one past the end.
  • ✗Forgetting that an unsorted array cannot use binary search.
3.6 Binary Search

On a sorted array, check the middle, then throw away the half that cannot contain the key. Each step halves the problem, so it takes O(log n).

Best caseO(1): key is at the first mid
Average / worst caseO(log n): about ⌊log₂ n⌋ + 1 comparisons
1,000,000 itemsat most 20 comparisons
Needs sorted data?Yes, and random access (an array, not a linked list)
SpaceO(1) iterative · O(log n) recursive (call stack)
  • ✗Using while (low < high) instead of low <= high. That misses a key when one item is left.
  • ✗Setting low = mid or high = mid instead of mid ± 1, which can loop forever.
  • ✗Running it on unsorted data. The result is meaningless.
3.7 Recursion Primer

A function that calls itself on a smaller problem until it reaches a base case. You need this for trees (Ch 6) and merge/quick sort (Ch 8).

factorial(n): timeO(n): n calls, O(1) work each
factorial(n): spaceO(n) stack frames
recursive binary search: timeO(log n)
recursive binary search: spaceO(log n) stack frames (iterative version: O(1))
  • ✗No base case, or a base case that is never reached (e.g. factorial(n + 1)).
  • ✗Forgetting to return the recursive call's result (bs(...) instead of return bs(...)).
  • ✗Thinking recursion is always slower or always better. It trades stack memory for simpler code.
Stable sort:
A sort that keeps equal keys in their original relative order.
In-place:
Uses only O(1) extra memory besides the input (recursion stack aside).
Pass:
One full sweep of the outer loop of a sort.
Pointer sort:
Sorting an array of pointers (or indices) to records instead of moving the records themselves.
Linear search:
Check each item in turn. O(n). Works on unsorted data.
Binary search:
Repeatedly halve a sorted range by comparing with the middle item. O(log n).
Recursion:
A function that calls itself on a smaller input, stopping at a base case.
Base case:
The input a recursive function answers directly, without recursing.
Call stack:
Memory where each active function call keeps its parameters and local variables.
04

Linked Lists

dsa.fetehadin.com
4.1 Pointers, new/delete & the Heap

A pointer holds an address. new asks the heap for memory that lives until you delete it. Master this and linked lists become easy.

&x, *p, p->fieldO(1)
new / delete one objectO(1) (amortised; done by the allocator)
Stack memoryautomatic: freed when the function returns
Heap memorymanual: lives until delete
  • ✗Dereferencing nullptr or an uninitialised pointer, which crashes (segmentation fault).
  • ✗Using memory after delete (dangling pointer). Set the pointer to nullptr right after.
  • ✗new[] paired with delete instead of delete[].
4.2 Singly Linked List

Nodes on the heap, each pointing to the next. Insert at front, end or a position, delete by value, search and traverse, plus the dummy-node trick and the classic pitfalls.

Insert at frontO(1)
Insert at endO(n), or O(1) with a tail pointer
Insert at position kO(k)
Delete by value / searchO(n)
Access the i-th itemO(n): no random access
SpaceO(n) plus one pointer per node
  • ✗Losing the list by updating head before linking the new node (head = n; first).
  • ✗Dereferencing temp->next without checking temp != nullptr.
  • ✗Forgetting the empty-list and delete-the-head special cases.
4.3 Doubly Linked List

Each node points both forward and backward. You can walk in both directions and delete a node knowing only its address, at the cost of one extra pointer per node.

Insert / delete at front or end (with tail)O(1)
Delete a node you already have a pointer toO(1): no need to find prev
Search / delete by valueO(n)
Traverse backwardO(n): impossible in a singly list without extra memory
SpaceO(n), with 2 pointers per node
  • ✗Updating next but forgetting prev (or the reverse). Each insert or delete touches both directions.
  • ✗Forgetting cur->next->prev when cur->next is nullptr (deleting the tail) → crash.
  • ✗Not updating tail when deleting the last node.
Pointer:
A variable that stores a memory address.
Heap (memory):
Memory for dynamic allocation with new/delete. Lives until you free it.
Memory leak:
Heap memory that is no longer reachable but was never deleted.
Dangling pointer:
A pointer to memory that has already been freed.
Node:
One element of a linked structure: data plus pointer(s) to other nodes.
Head:
Pointer to the first node of a linked list.
Dummy (sentinel) node:
A fake node placed before the head so every real node has a predecessor.
Doubly linked list:
Each node has both next and prev pointers, allowing backward traversal and O(1) delete given a node.
05

Stacks & Queues

dsa.fetehadin.com
5.1 Stacks with an Array

Last in, first out. The stack ADT (push, pop, peek, isEmpty, isFull) built on a fixed array with a top index, plus overflow and underflow.

pushO(1)
popO(1)
peek / isEmpty / isFullO(1)
search for a valueO(n): not a stack operation
SpaceO(MAX), fixed when the array is declared
  • ✗Writing st[top++] = x with top = -1 (writes to index −1). Use st[++top] = x.
  • ✗Popping without checking isEmpty() (underflow).
  • ✗Pushing without checking isFull() (writes past the array).
5.2 Stacks with a Linked List

The same stack ADT, but the top is the head of a linked list. No fixed capacity, push and pop stay O(1), and the bracket-matching application.

push (insert at head)O(1)
pop (delete head)O(1)
peek / isEmptyO(1)
SpaceO(n), one node (data + pointer) per item
  • ✗Using the tail as the top, making pop O(n).
  • ✗Forgetting delete in pop (memory leak).
  • ✗Reading top->data after deleting the node. Save the value first.
5.3 Queues with an Array

First in, first out. Why a naive array queue makes dequeue O(n), how a front index makes it O(1), and the wasted-slot problem that leads to the circular queue.

enqueue (at rear)O(1)
dequeue, naive (shift left)O(n)
dequeue, with front indexO(1)
front / isEmptyO(1)
SpaceO(MAX)
  • ✗Confusing front and rear (items enter at the rear, leave at the front).
  • ✗Dequeuing without checking for empty.
  • ✗With a front index, testing rear == -1 for empty. The correct test is front > rear.
5.4 Queues with a Linked List

Keep two pointers, front and rear. Enqueue at the rear and dequeue at the front, both O(1), with no capacity limit.

enqueue (insert after rear)O(1)
dequeue (delete front)O(1)
peek / isEmptyO(1)
SpaceO(n)
  • ✗Dequeuing from the tail (needs the previous node, so O(n)).
  • ✗Forgetting rear = nullptr when the queue becomes empty. The next enqueue then writes through a dangling pointer.
  • ✗Enqueue into an empty queue without setting both front and rear.
5.5 Circular Queue

Let front and rear wrap around with the modulo operator, so freed slots are reused. All operations O(1) on a fixed array.

enqueueO(1)
dequeueO(1)
front / isEmpty / isFullO(1)
SpaceO(N), and every slot is usable
  • ✗Writing rear++ instead of rear = (rear + 1) % N.
  • ✗Testing front == rear for both empty and full. Without a count (or one spare slot) they look the same.
  • ✗Printing from 0 to rear instead of starting at front and wrapping.
5.6 std::stack, std::queue & std::deque

The standard library versions you will use in real code and on LeetCode, their method names, and the classic traps (pop returns void, empty checks).

stack push / pop / topO(1)
queue push / pop / front / backO(1)
deque push/pop at either endO(1)
deque d[i]O(1)
SpaceO(n)
  • ✗Writing int x = s.pop();. It does not compile; pop() returns void. Read top() first.
  • ✗Calling top() / front() / pop() on an empty container (undefined behaviour). Check empty().
  • ✗Using q.top() on a queue (it is front()), or s.front() on a stack (it is top()).
Stack:
LIFO collection: push and pop at the top only.
LIFO:
Last In, First Out.
Overflow / underflow:
Pushing onto a full stack / popping from an empty one.
Queue:
FIFO collection: enqueue at the rear, dequeue from the front.
FIFO:
First In, First Out.
Circular queue:
Array queue whose indices wrap around with % N so freed slots are reused.
Deque:
Double-ended queue: insert and remove at both ends in O(1).
06

Trees

dsa.fetehadin.com
6.1 Binary Trees & Tree Terms

Root, parent, child, leaf, sibling, depth, height, level, subtree; full, complete and perfect binary trees; and the Node struct with recursive count and height.

count nodes / leavesO(n): visits every node once
heightO(n)
find a value (no ordering)O(n)
SpaceO(h) recursion stack, where h is the height
  • ✗Mixing up depth (from the root down to a node) and height (from a node down to its deepest leaf).
  • ✗Not stating your height convention. Here, a single node has height 0 and an empty tree −1. Some books count nodes instead of edges.
  • ✗Thinking "complete" and "full" mean the same thing.
6.2 Binary Search Tree

Left is smaller, right is bigger. Insert, search, find min and max by walking one path, and why the tree's shape decides whether that is O(log n) or O(n).

search / insert, balancedO(log n)
search / insert, skewedO(n)
min / maxO(h)
inorder (sorted output)O(n)
SpaceO(n) for the nodes, O(h) recursion
  • ✗Writing insert(root->left, x); without assigning the result back to root->left.
  • ✗Checking only a node's children, not the whole subtree, when asked "is this a BST?".
  • ✗Forgetting the nullptr check before reading root->data.
6.3 BST Delete (3 Cases)

Deleting a leaf, a node with one child, and a node with two children by copying the inorder successor. The most-asked BST exam question.

delete, balancedO(log n)
delete, skewedO(n)
SpaceO(h) recursion
  • ✗Case 3: copying the successor value but forgetting to delete the successor node itself (you get a duplicate).
  • ✗Case 3: searching for the successor in the left subtree. Successor = min of the right subtree.
  • ✗Not returning the new subtree root, so the parent still points at the deleted node.
6.4 Tree Traversals

Preorder, inorder and postorder (depth-first, recursive) and level order (breadth-first, with a queue). Know them by hand and in code.

pre / in / post orderO(n): every node once
level orderO(n)
SpaceO(h) recursion for DFS; O(w) queue for level order (w = widest level)
  • ✗Mixing the orders. Remember where the Node goes - pre = N L R, in = L N R, post = L R N.
  • ✗Forgetting that "left before right" is the same in all three.
  • ✗Using a stack for level order (that gives a DFS order instead).
6.5 General Trees (Left-Child, Right-Sibling)

Trees where a node can have any number of children, stored with only two pointers per node using the left-child/right-sibling representation.

visit all nodesO(n)
find the k-th childO(k)
count the children of a nodeO(degree)
SpaceO(n), exactly two pointers per node
  • ✗Linking a node's second child to the parent instead of to the first child's nextSibling.
  • ✗Drawing siblings as "right child of the parent".
  • ✗Forgetting that the root has no siblings.
Binary tree:
A tree in which every node has at most two children, left and right.
Root / leaf:
The top node (no parent) / a node with no children.
Depth / height:
Depth: edges from the root down to a node. Height: edges from a node down to its deepest leaf.
Full / complete / perfect:
Full: 0 or 2 children everywhere. Complete: all levels full except the last, filled from the left. Perfect: all levels full.
BST:
Binary search tree: for every node, left subtree < node < right subtree.
Inorder successor:
The next value in sorted order; for a node with a right child, the minimum of its right subtree.
Preorder / inorder / postorder:
Depth-first traversals: Node-Left-Right / Left-Node-Right / Left-Right-Node.
Level-order traversal:
Visit a tree level by level using a queue (BFS on a tree).
General tree:
A tree whose nodes may have any number of children.
Left-child, right-sibling:
Stores a general tree with two pointers per node: first child and next sibling.
07

Graphs

dsa.fetehadin.com
7.1 Graph Terminology

Vertices and edges, adjacency, degree, paths, cycles, connected graphs, weighted graphs, and how a tree is a special graph.

max edges, undirectedV(V − 1)/2
max edges, directedV(V − 1)
sum of all degrees2E (each edge counted at both ends)
SpaceDepends on the representation (next topic)
  • ✗Counting path length in vertices instead of edges.
  • ✗Saying every graph is connected.
  • ✗Forgetting that a cycle must not reuse an edge.
7.2 Adjacency Matrix & Adjacency List

Two ways to store a graph in memory, what each costs, and when to choose which.

spacematrix O(V²) · list O(V + E)
is there an edge u–v?matrix O(1) · list O(deg u)
visit all neighbours of umatrix O(V) · list O(deg u)
add an edgematrix O(1) · list O(1)
  • ✗Setting only m[u][v] for an undirected edge. Set m[v][u] too.
  • ✗Saying the list is always better. Edge lookup is O(1) only in the matrix.
  • ✗Forgetting that an undirected edge appears twice in the list.
7.3 Directed Graphs

One-way edges, in-degree and out-degree, directed paths and cycles, and how the matrix and list change.

out-degree of ulist O(1) (size of adj[u]) · matrix O(V) (row sum)
in-degree of ulist O(V + E) · matrix O(V) (column sum)
spacematrix O(V²) · list O(V + E)
  • ✗Adding both m[u][v] and m[v][u] for a directed edge.
  • ✗Following an arrow backwards when tracing a path.
  • ✗Mixing up in-degree (arrows coming in) and out-degree (arrows going out).
7.4 BFS & DFS

Breadth-first search with a queue, depth-first search with recursion or a stack, the visited array, and O(V + E).

BFS / DFS with adjacency listO(V + E)
BFS / DFS with adjacency matrixO(V²)
SpaceO(V) for visited plus the queue or stack
  • ✗No visited array, so the search loops forever around a cycle.
  • ✗In BFS, marking a vertex visited when it is dequeued instead of enqueued (it can be enqueued twice).
  • ✗Visiting neighbours in a different order from the one the question uses (usually ascending).
Graph:
G = (V, E): a set of vertices and a set of edges between them.
Degree:
Number of edges at a vertex. In a digraph, split into in-degree and out-degree.
Path / cycle:
A sequence of vertices joined by edges / a path that returns to its start.
Connected graph:
There is a path between every pair of vertices.
Adjacency matrix:
V×V table where m[u][v] = 1 (or the weight) if there is an edge u→v. O(V²) space.
Adjacency list:
For each vertex, a list of its neighbours. O(V + E) space.
Directed graph (digraph):
A graph whose edges are one-way arrows.
DAG:
Directed acyclic graph: a digraph with no directed cycles.
BFS:
Breadth-first search: explore neighbours in waves using a queue. Finds fewest-edge paths.
DFS:
Depth-first search: go as deep as possible, then backtrack, using recursion or a stack.
08

Advanced Sorting & Searching

dsa.fetehadin.com
8.1 Shell Sort

Insertion sort on items a gap apart, with the gap shrinking to 1. Far-away items move in big jumps, so the final insertion sort has little work left.

Best case (sorted)O(n log n)
Average casedepends on the gaps; about O(n^1.5) for n/2, n/4, …
Worst case (gaps n/2, n/4, …)O(n²)
SpaceO(1)
Stable · in-placeno · yes
  • ✗Comparing neighbours (gap 1) in every pass, which is just insertion sort.
  • ✗Forgetting that the last pass must use gap = 1, or the array may not be sorted.
  • ✗Claiming it is stable. Jumps over a gap can reorder equal keys.
8.2 Quick Sort

Pick a pivot, partition so smaller items go left and bigger items go right, then sort each side recursively. Average O(n log n), worst O(n²).

Best case (pivot splits in half)O(n log n)
Average caseO(n log n)
Worst case (sorted input, last-item pivot)O(n²)
SpaceO(log n) recursion on average, O(n) worst
Stable · in-placeno · yes
  • ✗Recursing on (lo, p) instead of (lo, p − 1). The pivot is already in place.
  • ✗Forgetting the final swap(a[i + 1], a[hi]) that puts the pivot in place.
  • ✗Saying quick sort is always O(n log n).
8.3 Merge Sort

Split the array in half, sort each half recursively, then merge the two sorted halves. Always O(n log n), stable, but needs O(n) extra space.

Best caseO(n log n)
Average caseO(n log n)
Worst caseO(n log n)
SpaceO(n) for the temporary array
Stable · in-placeyes · no
  • ✗Forgetting to copy the leftover items after one half runs out.
  • ✗Using < instead of <= in merge, which breaks stability.
  • ✗Base case lo > hi only. A single item (lo == hi) must also stop the recursion.
8.4 Heap Sort

Build a max-heap in the array, then repeatedly swap the root (the largest item) to the end and sift down. O(n log n) in every case, in-place.

Build heapO(n)
Each extract-max (swap + heapify)O(log n)
Best / average / worstO(n log n)
SpaceO(1) iterative, O(log n) with recursive heapify
Stable · in-placeno · yes
  • ✗Using 1-based formulas (2i, 2i+1) with a 0-based array. With 0-based it is 2i+1 and 2i+2.
  • ✗Starting the build loop at i = 0 instead of n/2 − 1, going down to 0.
  • ✗Calling heapify on the whole array instead of the shrinking heap size end.
8.5 Hashing Basics

A hash function turns a key into an array index, so search is O(1) on average. Collisions, separate chaining, linear probing and the load factor.

search / insert / delete, averageO(1)
search / insert / delete, worst (all keys collide)O(n)
SpaceO(M + n)
  • ✗With linear probing, stopping a search at a slot holding a different key. Only an empty slot means "not found".
  • ✗Forgetting to wrap around with % M when probing.
  • ✗Claiming hashing is always O(1). The worst case is O(n).
Divide and conquer:
Split a problem into smaller parts, solve them recursively, combine the results.
Pivot:
The item quick sort partitions around.
Partition:
Rearranging a range so items smaller than the pivot come before it and bigger ones after.
Max-heap:
A complete binary tree where every parent is ≥ its children; stored in an array.
Heapify:
Sift a node down until the heap property holds below it. O(log n).
Gap (shell sort):
The distance between compared items in a shell sort pass; shrinks to 1.
Hash function:
Maps a key to an index of the hash table, e.g. h(k) = k mod M.
Collision:
Two different keys hashing to the same index.
Separate chaining:
Each table slot holds a list of all keys that hash there.
Linear probing:
On a collision, try the next slot (h + i) % M until an empty one.
Load factor:
α = n / M, how full a hash table is.

Keyboard shortcuts

⌘K /
Search topics, glossary, cheat sheets
space
Play / pause the visualizer
← →
Step back / forward
r
Reset the visualizer
?
Show this help