Sorting: How & When
Sorting is the most-studied problem in computer science, and the ideas inside the classic algorithms — divide and conquer, trading memory for speed — show up everywhere. You'll almost always call your language's built-in sort, but knowing what's under it makes you use it well.
The simple sorts are O(n²): bubble sort repeatedly swaps neighbors that are out of order; insertion sort takes each item and slides it back into place among the already-sorted prefix (how people sort cards — and genuinely good on small or nearly-sorted data). They're worth reading once to see why they're slow: both compare almost every pair.
The fast sorts hit O(n log n) with divide and conquer plus recursion. Merge sort: split the list in half, recursively sort each half, then merge two sorted halves in one linear pass. The log n comes from halving (like binary search), the n from merging each level. It's the cleanest recursive algorithm in this track.
Quicksort is merge sort's rival: pick a pivot, partition items into smaller-than and bigger-than, recurse on each side. Faster in practice (in-place, cache-friendly) but O(n²) worst case on adversarial input. Real standard libraries use hybrids — Python's sorted() uses Timsort (merge sort + insertion sort, exploits already-sorted runs), and it's stable: equal items keep their original order, so you can sort by one key then another.
That's the core toolkit: Big-O to measure, arrays and hash maps for storage, stacks and queues for order, recursion for self-similar problems, trees and graphs for connected data, sorting to impose order. From here, practice beats theory — pick easy problems on any practice site, and reach for the structure whose trade-offs fit. You now know them all.