In algorithms, we don't just care about getting the right answer; we care about how much it costs to get there.
Big O Notation: Predictive Power
Big O notation allows us to describe the upper bound of an algorithm's running time in terms of the input size $n$. It is the universal language of complexity.
- O(1): Constant (Instant)
- O(log n): Logarithmic (Binary Search)
- O(n): Linear (Simple Scan)
- O(n log n): Linearithmic (Efficient Sorting)
- O(n²): Quadratic (Nested Loops)
The Sorting Spectrum
Sorting is the cornerstone of algorithmic thinking. Most complex problems become trivial once the data is ordered.
Bubble Sort (The Intuitive Path)
- Time: O(n²)
- Logic: Compare adjacent elements and swap if they are in the wrong order. Repeat until stable.
public void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// Swap arr[j] and arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
Merge Sort (Divide & Conquer)
- Time: O(n log n)
- Logic: Split the array into halves, sort each half recursively, and merge them back together. Reliable and stable.
Quick Sort (The High-Speed Gamble)
- Time: O(n log n) average / O(n²) worst-case
- Logic: Pick a pivot, partition the array, and recursively sort the partitions. Extremely fast in practice.
"The difference between O(n) and O(n log n) is the difference between a project that succeeds and one that fails."