What you'll learn
Algorithms (step-by-step procedures) and data structures (ways to organise data) are the bedrock of computer science — and the heart of coding interviews. This module gives you the essential mental models and the ability to reason about efficiency.
By the end you'll be able to:
- Analyse efficiency with Big-O notation
- Choose the right data structure for a task
- Implement and compare searching and sorting algorithms
- Apply core techniques: recursion, divide & conquer, greedy, dynamic programming
Big-O notation
Big-O describes how an algorithm's time (or memory) grows as the input grows. It ignores constants and focuses on the shape of growth — because at scale, that shape is what matters. An O(n²) algorithm that's fine for 100 items may be unusable for a million:
| Big-O | Name | Example |
|---|---|---|
O(1) | Constant | array index, HashMap.get |
O(log n) | Logarithmic | binary search |
O(n) | Linear | a single loop over the data |
O(n log n) | Linearithmic | efficient sorts (merge, quick) |
O(n²) | Quadratic | nested loops, bubble sort |
O(2ⁿ) | Exponential | naive recursive Fibonacci |
Core data structures
You met the collection classes in Module 14 — here's how they perform. Choosing the right structure is often the single biggest performance decision you'll make:
| Structure | Access | Search | Insert/Delete |
|---|---|---|---|
Array / ArrayList | O(1) | O(n) | O(n) (O(1) at the end) |
LinkedList | O(n) | O(n) | O(1) at the ends |
HashMap / HashSet | — | O(1) avg | O(1) avg |
TreeMap / TreeSet | — | O(log n) | O(log n) |
Stack / Queue | — | O(n) | O(1) |
Note
PriorityQueue), and graphs (networks of nodes and edges) power everything from file systems to maps and social networks.Searching
Linear search checks every element — O(n), works on any data. Binary search is dramatically faster — O(log n) — but requires sorted data. It repeatedly halves the search space, so finding an item among a billion takes only ~30 steps:
static int binarySearch(int[] arr, int target) {
int lo = 0, hi = arr.length - 1;
while (lo <= hi) {
int mid = (lo + hi) / 2;
if (arr[mid] == target) return mid; // found
if (arr[mid] < target) lo = mid + 1; // search right half
else hi = mid - 1; // search left half
}
return -1; // not found
}
public static void main(String[] args) {
int[] sorted = {1, 3, 5, 7, 9, 11}; // MUST be sorted
System.out.println(binarySearch(sorted, 7)); // -> 3
System.out.println(binarySearch(sorted, 4)); // -> -1
}Sorting
Sorting is a classic study in trade-offs. Simple sorts like bubble and insertion sort are O(n²) — easy to write, slow at scale. Efficient sorts like merge sort and quicksort are O(n log n) and are what real libraries use.
Tip
Arrays.sort() and Collections.sort() use highly-optimised O(n log n) algorithms. But understanding how they work (and their cost) is essential for reasoning about performance and acing interviews.Key techniques
A handful of problem-solving strategies unlock most algorithmic problems:
- Recursion — solve a problem via smaller versions of itself (Module 6)
- Divide & conquer — split, solve the parts, combine (merge sort, binary search)
- Greedy — take the locally best choice at each step (works for some problems)
- Dynamic programming — cache overlapping subproblems so you never recompute them
Naive recursive Fibonacci is O(2ⁿ) because it recomputes the same values endlessly. Adding a cache — memoization, the essence of dynamic programming — makes it O(n):
static long[] memo = new long[50];
static long fib(int n) {
if (n <= 1) return n;
if (memo[n] != 0) return memo[n]; // reuse cached result
return memo[n] = fib(n - 1) + fib(n - 2); // compute once, store
}
public static void main(String[] args) {
System.out.println(fib(40)); // instant, thanks to memoization
}Recap & quick check
Key takeaways
- Big-O describes how cost grows with input size, ignoring constants (O(1) < O(log n) < O(n) < O(n log n) < O(n²)).
- Match the data structure to the task: HashMap for O(1) lookup, ArrayList for indexed access, tree for sorted.
- Binary search is O(log n) but needs sorted data; linear search is O(n) on anything.
- Efficient sorts are O(n log n); libraries' Arrays.sort/Collections.sort already implement them.
- Recursion, divide & conquer, greedy, and dynamic programming (memoization) solve most problems.
Quick check
1. What does Big-O notation describe?
2. What's the time complexity of binary search?
3. Which structure gives average O(1) lookup by key?
4. Efficient general-purpose sorting algorithms run in…
5. Caching overlapping subproblem results is the core of…
Excellent — you can now reason about efficiency and pick the right tools. Next up: Module 36 — Security Fundamentals.